39

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?

ॐ Rakesh Kumar
  • 1,318
  • 1
  • 14
  • 24
Caspar Geerlings
  • 1,021
  • 2
  • 10
  • 21
  • why not have the same function get called when clicking the center bottom navigation view button that's getting called when clicking the fab icon? – hsm59 Dec 20 '18 at 08:04
  • Since the middle navigation item is invisble, this was a possible solution I thought of as well. The FAB is clickable/focusable so in the perfect scenario, I don't want a ripple effect on the button when clicked underneath it. – Caspar Geerlings Dec 20 '18 at 08:06
  • `BottomAppBar` is just a specialized `Toolbar`. Have you considered trying to add your own labeled buttons directly to that? Seems a little less hacky than overlaying a `BottomNavigationView`. – Mike M. Dec 20 '18 at 08:25
  • The reason of this hacky approach is to keep the behaviour of the FAB in the BottomAppBar. (E.G. animations). Perhaps someone has a useful solution for me, otherwise I should use my own button. – Caspar Geerlings Dec 20 '18 at 08:33
  • No, I mean, keep the `BottomAppBar` (along with its `FloatingActionButton`), and add buttons to it, instead of overlaying the `BottomNavigationView`. It shouldn't affect the FAB. – Mike M. Dec 20 '18 at 08:38
  • Is there a reason you aren't following the guide here https://material.io/design/components/app-bars-bottom.html#anatomy ? – Zun Dec 20 '18 at 08:57
  • @ZUNJAE Yes, I want to have a navigation menu with items including icon+title. – Caspar Geerlings Dec 20 '18 at 09:12
  • How can you handle the FAB actions like the other items on the bottom nav ? – kinsley kajiva Apr 17 '21 at 23:48

6 Answers6

28

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

AskNilesh
  • 67,701
  • 16
  • 123
  • 163
28

There is an easier way to combine those 3 widgets. We could have something like:

<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/coordinator"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="?attr/actionBarSize" />

    <com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bottomBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:backgroundTint="?attr/colorPrimary"
        app:contentInsetEnd="0dp"
        app:contentInsetStart="0dp"
        app:fabAlignmentMode="end">

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottomNavigation"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:backgroundTint="@android:color/transparent"
            app:elevation="0dp"
            android:layout_marginEnd="100dp"
            app:itemIconTint="@android:color/white"
            app:itemRippleColor="@android:color/white"
            app:itemTextColor="@android:color/white"
            app:menu="@menu/menu_activity_home_bottom_navigation" />

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

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


</androidx.coordinatorlayout.widget.CoordinatorLayout>

And that's it.

Depending on where the FAB is anchored (CENTER|END) the menu icons will be placed either to the left or in the middle:

For this sample, the result would be something like: enter image description here

If you think there's a better way, please feel free to edit/comment so I can fix the post :)

cesards
  • 15,882
  • 11
  • 70
  • 65
  • 2
    It doesn't work when the FAB is anchored in the CENTER. – Shefchenko May 26 '20 at 09:06
  • 2
    @Shefchenko it works fine with center mode. Just use ```app:fabAlignmentMode="center"``` – Pavlo28 Dec 03 '20 at 14:08
  • @Shefchenko To work in the middle just add `app:fabAlignmentMode="center"` as GrafOrlov said and remove also the `android:layout_marginEnd="100dp"` – lujop Feb 15 '21 at 09:00
  • This doesn't seem to work on API 21, on my phone running the latest version of Android works perfectly, but running it in an emulator on API 21 shows the BottomAppBar twice as big and the BottomNavigationView stays pinned at the top as if it had a margin_bottom – Tiago Oliveira Mar 31 '21 at 05:19
  • How can you handle the FAB actions like the other items on the bottom nav ? – kinsley kajiva Apr 17 '21 at 23:48
8

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>
Artur Antonyan
  • 578
  • 6
  • 11
4

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

miel3k
  • 211
  • 3
  • 3
1

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>
Greenonline
  • 1,330
  • 8
  • 23
  • 31
Skarlet
  • 51
  • 4
0

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>
felislynx.silae
  • 147
  • 4
  • 4