android – How to combine BottomAppBar + FAB with BottomNavigationView-ThrowExceptions

Exception or error:

I want to use the FloatingActionButton, along with its behaviour when anchored on a BottomAppBar, on top of a BottomNavigationView.

I came up with a rather “hacky” trick to just place the BottomNavigationView on top of the BottomAppBar without providing a background thus making it transparent.

This seemed to work well at first sight but I found out that the fab button can only be clicked when touching the upper half of the button (So where there is no transparent BottomNavigationView on top).

Screenshot

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:layout_gravity="bottom"
        app:layout_constraintBottom_toBottomOf="parent">

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:clickable="true"
            android:focusable="true"
            app:layout_anchor="@id/bar" />

        <com.google.android.material.bottomappbar.BottomAppBar
            android:id="@+id/bar"
            android:layout_width="match_parent"
            android:layout_height="58dp"
            android:layout_gravity="bottom"
            android:backgroundTint="@color/colorPrimaryDark" />

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottomNavigation"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            app:itemIconTint="@android:color/darker_gray"
            app:itemTextColor="@android:color/white"
            app:labelVisibilityMode="labeled"
            app:menu="@menu/navigation" />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Is there any way of implementing this idea in which I can fully click on the FloatingActionButton?

How to solve:

First Way

Try this You can Create a CustomBottomNavigationView

Here is the good article for CustomBottomNavigationView

How I draw custom shapes in BottomNavigationView

SAMPLE CODE

import android.content.Context;
import android.graphics.*;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;

public class CustomBottomNavigationView extends BottomNavigationView {

    private Path mPath;
    private Paint mPaint;

    /** the CURVE_CIRCLE_RADIUS represent the radius of the fab button */
    private final int CURVE_CIRCLE_RADIUS = 128 / 2;
    // the coordinates of the first curve
    private Point mFirstCurveStartPoint = new Point();
    private Point mFirstCurveEndPoint = new Point();
    private Point mFirstCurveControlPoint1 = new Point();
    private Point mFirstCurveControlPoint2 = new Point();

    //the coordinates of the second curve
    @SuppressWarnings("FieldCanBeLocal")
    private Point mSecondCurveStartPoint = new Point();
    private Point mSecondCurveEndPoint = new Point();
    private Point mSecondCurveControlPoint1 = new Point();
    private Point mSecondCurveControlPoint2 = new Point();
    private int mNavigationBarWidth;
    private int mNavigationBarHeight;

    public CustomBottomNavigationView(Context context) {
        super(context);
        init();
    }

    public CustomBottomNavigationView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CustomBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(ContextCompat.getColor(getContext(),R.color.colorAccent));
        setBackgroundColor(Color.TRANSPARENT);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // get width and height of navigation bar
        // Navigation bar bounds (width & height)
        mNavigationBarWidth = getWidth();
        mNavigationBarHeight = getHeight();
        // the coordinates (x,y) of the start point before curve
        mFirstCurveStartPoint.set((mNavigationBarWidth / 2) - (CURVE_CIRCLE_RADIUS * 2) - (CURVE_CIRCLE_RADIUS / 3), 0);
        // the coordinates (x,y) of the end point after curve
        mFirstCurveEndPoint.set(mNavigationBarWidth / 2, CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4));
        // same thing for the second curve
        mSecondCurveStartPoint = mFirstCurveEndPoint;
        mSecondCurveEndPoint.set((mNavigationBarWidth / 2) + (CURVE_CIRCLE_RADIUS * 2) + (CURVE_CIRCLE_RADIUS / 3), 0);

        // the coordinates (x,y)  of the 1st control point on a cubic curve
        mFirstCurveControlPoint1.set(mFirstCurveStartPoint.x + CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4), mFirstCurveStartPoint.y);
        // the coordinates (x,y)  of the 2nd control point on a cubic curve
        mFirstCurveControlPoint2.set(mFirstCurveEndPoint.x - (CURVE_CIRCLE_RADIUS * 2) + CURVE_CIRCLE_RADIUS, mFirstCurveEndPoint.y);

        mSecondCurveControlPoint1.set(mSecondCurveStartPoint.x + (CURVE_CIRCLE_RADIUS * 2) - CURVE_CIRCLE_RADIUS, mSecondCurveStartPoint.y);
        mSecondCurveControlPoint2.set(mSecondCurveEndPoint.x - (CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4)), mSecondCurveEndPoint.y);

        mPath.reset();
        mPath.moveTo(0, 0);
        mPath.lineTo(mFirstCurveStartPoint.x, mFirstCurveStartPoint.y);

        mPath.cubicTo(mFirstCurveControlPoint1.x, mFirstCurveControlPoint1.y,
                mFirstCurveControlPoint2.x, mFirstCurveControlPoint2.y,
                mFirstCurveEndPoint.x, mFirstCurveEndPoint.y);

        mPath.cubicTo(mSecondCurveControlPoint1.x, mSecondCurveControlPoint1.y,
                mSecondCurveControlPoint2.x, mSecondCurveControlPoint2.y,
                mSecondCurveEndPoint.x, mSecondCurveEndPoint.y);

        mPath.lineTo(mNavigationBarWidth, 0);
        mPath.lineTo(mNavigationBarWidth, mNavigationBarHeight);
        mPath.lineTo(0, mNavigationBarHeight);
        mPath.close();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(mPath, mPaint);
    }
}

Now use like this

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorlayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerInParent="true"
        android:layout_marginBottom="30dp"
        android:clickable="true"
        android:focusable="true" />

    <neel.com.demo.CustomBottomNavigationView
        android:id="@+id/customBottomBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="@color/colorAccent"
        app:labelVisibilityMode="labeled" />


</RelativeLayout>

Activity

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        CustomBottomNavigationView curvedBottomNavigationView = findViewById(R.id.customBottomBar);
        curvedBottomNavigationView.inflateMenu(R.menu.bottom_menu);
    }
}

OUTPUT

enter image description here

Second Way

    <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="120dp"
    android:layout_gravity="bottom">

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:focusable="true"
        app:layout_anchor="@id/bar" />

    <com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bar"
        android:layout_width="match_parent"
        android:layout_height="58dp"
        android:layout_gravity="bottom"
        android:backgroundTint="@color/colorPrimaryDark">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                style="?android:attr/borderlessButtonStyle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="?android:attr/selectableItemBackground"
                android:drawableTop="@drawable/ic_favorite"
                android:gravity="center"
                android:orientation="vertical"
                android:text="Personal"
                android:textColor="#FFFFFF">

            </TextView>

            <TextView
                style="?android:attr/borderlessButtonStyle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="?android:attr/selectableItemBackground"
                android:drawableTop="@drawable/ic_favorite"
                android:gravity="center"
                android:orientation="vertical"
                android:text="Personal"
                android:textColor="#FFFFFF">

            </TextView>

            <TextView
                style="?android:attr/borderlessButtonStyle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="?android:attr/selectableItemBackground"
                android:drawableTop="@drawable/ic_favorite"
                android:gravity="center"
                android:orientation="vertical"
                android:textColor="#FFFFFF"
                android:visibility="invisible">

            </TextView>

            <TextView
                style="?android:attr/borderlessButtonStyle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="?android:attr/selectableItemBackground"
                android:drawableTop="@drawable/ic_favorite"
                android:gravity="center"
                android:orientation="vertical"
                android:text="Personal"
                android:textColor="#FFFFFF">

            </TextView>

            <TextView
                style="?android:attr/borderlessButtonStyle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="?android:attr/selectableItemBackground"
                android:drawableTop="@drawable/ic_favorite"
                android:gravity="center"
                android:orientation="vertical"
                android:text="Personal"
                android:textColor="#FFFFFF">

            </TextView>

        </LinearLayout>

    </com.google.android.material.bottomappbar.BottomAppBar>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

OUTPUT

enter image description here

###

I found a quicker solution. I wrapped bottomnavigationview in frameLayout and everything work as expected. Try this:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.coordinatorlayout.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/lt_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:fitsSystemWindows="false">


    <ViewPager
        android:id="@+id/main_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/bottom_navigation"
        android:layout_alignParentStart="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bottom_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:clickable="false"
        app:fabAlignmentMode="center" />


    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom">

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_navigation"
            style="@style/BottomNavigationStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:clickable="false"
            app:menu="@menu/bottom_menu" />


    </FrameLayout>


    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_anchor="@id/bottom_bar" />


</androidx.coordinatorlayout.widget.CoordinatorLayout>

###

I tried to do this for several days and in the end I came to make a FAB with an empty BottomAppBar, on top of which a BottomNavigationView with a transparent background was superimposed.

In my case, the code looks like this:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bottom_app_bar"
        style="@style/Widget.MaterialComponents.BottomAppBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:backgroundTint="@color/colorGray"
        app:fabAlignmentMode="center" />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom">

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/nav_view"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:layout_gravity="bottom"
            android:background="#80FFFFFF"
            android:icon="@drawable/bottom_nav_ic_assignment"
            app:itemIconTint="@color/bottom_nav_item_color"
            app:itemTextColor="@color/bottom_nav_item_color"
            app:labelVisibilityMode="selected"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:menu="@menu/bottom_nav_menu" />

    </FrameLayout>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/addFab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:focusable="true"
        android:onClick="onFabClicked"
        android:src="@drawable/ic_add_white"
        app:backgroundTint="@color/colorBlue"
        app:fabSize="auto"
        app:layout_anchor="@+id/bottom_app_bar"
        app:layout_anchorGravity="center|top" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

###

To all who use second approach from Nilesh Rathod comment and want remove unexpected space before first text view:

Just set app:contentInsetStart="0dp" for BottomAppBar

###

Well you can treat BottomAppBar as Toolbar, which means that, this class extends ViewGroup so you can add relative layout, constraint layout etc inside BottomAppBar tag. Here is fragment of code that will show 2 buttons inside BottomAppBar.

<com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bar"
        style="@style/Widget.MaterialComponents.BottomAppBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:backgroundTint="@color/cardview_dark_background"
        app:fabAlignmentMode="center">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:id="@+id/leftbutton"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_marginRight="32dp"
                android:text="LEFT"
                android:textColor="@android:color/white"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toLeftOf="@+id/rightbutton"
                app:layout_constraintTop_toTopOf="parent" />

            <Button
                android:id="@+id/rightbutton"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_marginLeft="32dp"
                android:layout_marginRight="16dp"
                android:text="RIGHT"
                android:textColor="@android:color/white"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toRightOf="@+id/leftbutton"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </com.google.android.material.bottomappbar.BottomAppBar>

Leave a Reply

Your email address will not be published. Required fields are marked *