Search code examples
androidandroid-layoutandroid-linearlayout

How to make a 'wavy' border for a Linear layout in android studio?


This question is about mobile app UI design, I've searched the web for long time for this question and didn't found any satisfying explanation - so i decided to bring it here. I'm using Android studio (Java), I have a navigation bar that is made of a LinearLayout that contains 5 navigation buttons in it inside my activity_home.xml, I want to make kind of a rounded 'wavy' border in the middle (not corners) of that navigation bar. something that will look like this:

enter image description here

I will share here some code and images of what i already have and what i want it to be.

Code:

activity_home.xml:

<LinearLayout
    android:id="@+id/navigationBar"
    android:layout_width="match_parent"
    android:layout_height="70dp"
    android:background="@drawable/navigation_bar_border"
    android:orientation="horizontal"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="1">

    <ImageView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".20"
        android:background="@drawable/user_icon"
        android:clickable="true" />

    <ImageView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".20"
        android:background="@drawable/notifications_icon"
        android:clickable="true" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".20"
        android:background="@drawable/add_icon"
        android:clickable="true" />

    <ImageView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".20"
        android:background="@drawable/search_icon"
        android:clickable="true" />

    <ImageView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".20"
        android:background="@drawable/home_icon"
        android:clickable="true" />

</LinearLayout>

navigation_bar_border.xml:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <shape xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle" >
            
            <corners
                android:bottomLeftRadius="0dp"
                android:bottomRightRadius="0dp"
                android:topLeftRadius="10dp"
                android:topRightRadius="10dp" />

            <gradient
                android:startColor="@color/light_dark_color"
                android:endColor="@color/light_dark_color"
                android:angle="90"/>

        </shape>
    </item>
</layer-list>

What i have now:

enter image description here

What i want to achieve:

enter image description here

How can i do that?


Solution

  • Ah, that can be achieved by using BottomAppBarTopEdgeTreatment. How? Let's see.

    First, create a function like this which returns a MaterialShapeDrawable which you can set to the LinearLayout as:

    //Suppressing a Restricted API warning as it's a part of Stock Material Libraries.
    @SuppressLint("RestrictedApi")
    private fun returnBottomEdgeShapeDrawable(): MaterialShapeDrawable {
    
        //This is how you get the curve, the main ingredient.
        //BottomAppBarTopEdgeTreatment() takes three parameters:
        //FabMargin - Margin between Fab Button and the curve
        //RoundedCornerRadius - Radius to make the corners round
        //CradleVerticalOffset - Vertical offset of curve and Fab
        val bottomAppBarTopEdgeTreatment = BottomAppBarTopEdgeTreatment(
            2f, 8f, 2f
        )
    
        //Diameter of the curve, current as Default Fab 56Dp
        bottomAppBarTopEdgeTreatment.fabDiameter = resources.getDimension(R.dimen.56dp)
    
        //This is further the shape of the LinearLayout.
        //Set Corners to 0 in your case else corners of the LinearLayout will be curved
        val shapeAppearanceModel = ShapeAppearanceModel()
            .toBuilder()
            .setAllCornerSizes(0f)
            //Including the main ingredient here
            .setTopEdge(bottomAppBarTopEdgeTreatment)
            .build()
    
        //Returning the ShapeDrawable
        return MaterialShapeDrawable(shapeAppearanceModel)
    }
    

    This is exact code from one of my projects. Now, use it for your LinearLayout as:

    ViewCompat.setBackground(yourLinearLayout, returnBottomEdgeShapeDrawable())
    

    Remember to give the LinearLayout a BackgroundTint as the above code will make the shape but in Black color.

    I've commented the code to make it more understandable.

    Edit: One tip, you can use it for other Views as well like ImageView and also, you can show the curve in other sides as well like left or on bottom, just use setBottomEdge() instead of setTopEdge(). Also, this is the exact curve you've shown aboe in the images as this is the default code of Material App Bar shared by the one of the core developers of Material Libraries, Gabriele Mariotti.