76

I am facing some problems with new bottom bar.
I can't force to move the snackbar above the bottom bar (this is how design guideline told me should be https://www.google.com/design/spec/components/bottom-navigation.html#bottom-navigation-specs).

This is my activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">

<include
    layout="@layout/app_bar_main_activity"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

<android.support.design.widget.NavigationView
    android:id="@+id/nav_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    android:fitsSystemWindows="true"
    app:headerLayout="@layout/nav_header_main_activity"
    app:menu="@menu/activity_main_drawer" />

</android.support.v4.widget.DrawerLayout>

This is my app_bar_main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="test.tab_activity">

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingTop="@dimen/appbar_padding_top"
    android:theme="@style/MyAppTheme.NoActionBar.AppBarOverlay">

    <android.support.v7.widget.Toolbar
        android:id="@+id/main_activity_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|enterAlways"
        app:popupTheme="@style/MyAppTheme.NoActionBar.PopupOverlay">

    </android.support.v7.widget.Toolbar>

</android.support.design.widget.AppBarLayout>



<android.support.v4.view.ViewPager
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

<LinearLayout 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"
    android:orientation="vertical">

    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="@dimen/fab_margin"
        android:src="@drawable/ic_add_white_24dp" />

    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        style="@style/AppTabLayout"
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:background="?attr/colorPrimary"
        />

</LinearLayout>

The snackbar in main_activity.java looks like this

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(findViewById(R.id.main_content), "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });

Wrong...snackbar should be above bottom bar

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
Wladislaw
  • 1,200
  • 2
  • 12
  • 23

13 Answers13

79

With the material components library you can use the setAnchorView method to make a Snackbar appear above a specific view.

In your case if you are using a BottomAppBar and a fab, you should define the fab in the setAanchorView. Something like:

FloatingActionButton fab = findViewById(R.id.fab);
Snackbar snackbar = Snackbar.make(view, "Snackbar over BottomAppBar", Snackbar.LENGTH_LONG);
snackbar.setAnchorView(fab);

The result:

enter image description here

With a BottomNavigationView you can define it as anchorView:

    Snackbar snackbar = Snackbar.make(view,"Snackbar over BottomNav",Snackbar.LENGTH_INDEFINITE);
    snackbar.setAnchorView(bottomNavigationView);
    snackbar.show();

Result:

enter image description here

Gabriele Mariotti
  • 320,139
  • 94
  • 887
  • 841
  • 1
    Cool. Work for me. – Long Pham Jun 12 '20 at 04:42
  • 5
    @Wladislaw Can you please mark this as the accepted answer? It's the right way to do it. And thanks a lot Gabriele! – Toufic Batache Jan 13 '21 at 12:52
  • 4
    This is the best answer out of all of them – DIRTY DAVE Feb 26 '21 at 12:09
  • 2
    Best answer. Was looking for this for a day! – kalan nawarathne Aug 11 '21 at 10:15
  • 2
    How can I use this approach when the bottomNavigationBar is not in the class that creates the SnackBar? I use a single activity multiple fragments approach and the bottonNaviationBar is in the view binding of the single activity while the Snackbar is called from a Fragment (via a normal Java class that is not a View class, thus I can't use findViewByID). – VanessaF Jul 31 '22 at 08:59
  • Is it possible to pass the snackbar behind the menu? Giving the feeling that it is starting from the top of the menu? – Vinithius Dec 20 '22 at 12:05
47

You can do this programmatically without cluttering your xml with extra CoordinatorLayouts by changing the snackbar's margins.

Java example:

Snackbar snack = Snackbar.make(findViewById(R.id.coordinatorLayout), 
    "Your message", Snackbar.LENGTH_LONG);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) 
    snack.getView().getLayoutParams();
params.setMargins(leftMargin, topMargin, rightMargin, bottomBar.height);
snack.getView().setLayoutParams(params);
snack.show();

Kotlin single line:

Snackbar.make(coordinatorLayout, "Your message", Snackbar.LENGTH_LONG).apply {view.layoutParams = (view.layoutParams as CoordinatorLayout.LayoutParams).apply {setMargins(leftMargin, topMargin, rightMargin, bottomBar.height)}}.show()
Jim Pekarek
  • 7,270
  • 6
  • 33
  • 34
  • I have similar approach, just use bottomMargin of snackbar layout params http://stackoverflow.com/a/40686399/3304280 – Cheng Nov 18 '16 at 21:54
  • 1
    in my case it works, but the snack slides over the bottom bar, not behind it. – Carlos Hernández Gil Dec 03 '16 at 13:16
  • 2
    @CarlosHernándezGil It slides over because your view at the bottom has a lower elevation / Z-property. Snackbar's have an elevation of 6dp. Increase your view's elevation by setting the elevation property higher than 6dp in XML or a stateListAnimator. See [Android Documentation](https://developer.android.com/guide/topics/graphics/prop-animation.html#property-vs-view) for working with property values animation and visit [Material Design Guidelines](https://material.io/guidelines/material-design/elevation-shadows.html#elevation-shadows-elevation-android) for elevations. – Chad Mx Sep 18 '17 at 05:59
  • This worked for me. Thank you. Should be the accepted answer. – Chad Mx Sep 18 '17 at 06:04
45

Assuming your are working with CoordinatorLayout you can modify the Snackbar's layoutparams before calling show(). By setting the anchorId and anchorGravity the snackBar will display above the bottom nav bar:

val layoutParams = snackbar.view.layoutParams as CoordinatorLayout.LayoutParams
layoutParams.anchorId = R.id.navigation //Id for your bottomNavBar or TabLayout
layoutParams.anchorGravity = Gravity.TOP
layoutParams.gravity = Gravity.TOP
snackbar.view.layoutParams = layoutParams
jesusF10
  • 554
  • 5
  • 4
  • 3
    I believe this should be the accepted answer, it's a very elegant solution. – RobertoAllende Jun 19 '18 at 14:38
  • 3
    This is the recommended solution [by Google on Material.io](https://material.io/develop/android/components/snackbar/#anchoring-a-snackbar) – Joshua King Jan 15 '19 at 16:14
  • This is an elegant solution. I agree that it should be the accepted answer too. – Richard Dec 10 '20 at 17:10
  • The [recommended solution](https://material.io/components/snackbars/android#using-snackbars) mentioned above indeed suggests `.setAnchorView(...)` – Ryan W Nov 16 '21 at 00:42
34

replace your xml ->

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="test.tab_activity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/main_activity_toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways">

        </android.support.v7.widget.Toolbar>

    </android.support.design.widget.AppBarLayout>



    <android.support.v4.view.ViewPager
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:id="@+id/placeSnackBar">

            <android.support.v4.view.ViewPager
                android:id="@+id/view_pager"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1" />

            <android.support.design.widget.FloatingActionButton
                android:id="@+id/fab"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="end|bottom"
                android:layout_margin="@dimen/fab_margin"
                android:src="@drawable/ic_menu_gallery" />

        </android.support.design.widget.CoordinatorLayout>

        <android.support.design.widget.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="56dp"
            android:background="?attr/colorPrimary" />

    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>

and The Snackbar code will be

Snackbar.make(findViewById(R.id.placeSnackBar), "Replace with your own action", Snackbar.LENGTH_LONG)
            .setAction("Action", null).show();
Zahidul Islam
  • 3,180
  • 1
  • 25
  • 35
  • 1
    i am facing the problem, that my layout of my fragment is hidden, when i use the method from you. My layout is just somewhere, where i can not see it. Any ideas? It is maybe new question, but it has something to do with your answer. – Wladislaw Mar 31 '16 at 16:07
  • You mean the Viewpager? – Zahidul Islam Mar 31 '16 at 19:49
  • Yes, the viewpager is moved (to the top probably?). So i don't see my layout if i use coordinatorlayout there. – Wladislaw Mar 31 '16 at 22:15
  • 2
    doesn't work for me . I have a tablayout inside the root coordinator layout and trying to show the snackbar above the bottom tablayout. – rahul.ramanujam Aug 12 '16 at 00:04
  • This is indeed a very good answer, just pass nested coordinator-layout view to make() method of snackbar and snack bar will start showing from there. – Ali Ashraf Mar 10 '17 at 11:33
16

There is great article about how to use it HERE. There you will know how to make snackbar above BottomNavigationBar

Basically the code below presents most common usage of Toolbar together with BottomNavigationBar and FrameLayout as Fragment container

Important! Notice that

  1. fab button uses anchor to be placed correcty and useCompactPadding to preserve margins
  2. BottomNavigationView uses layout_behaviour to handle scrolling and SnackBar position

    <android.support.design.widget.AppBarLayout
        android:id="@+id/myAppBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:descendantFocusability="beforeDescendants"
        android:focusableInTouchMode="true"
        android:theme="@style/AppTheme.AppBarOverlay">
    
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:contentInsetStart="0dp"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>
    </android.support.design.widget.AppBarLayout>
    
    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />
    
    <android.support.design.widget.BottomNavigationView
        android:id="@+id/navigation_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:menu="@menu/bottom_navigation"
        app:layout_behavior="murt.shoppinglistapp.ui.BottomNavigationBehavior"
        android:background="?android:attr/windowBackground"
        />
    
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab_add_shopping_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:useCompatPadding="true"
        app:srcCompat="@drawable/ic_add_24"
        app:layout_anchor="@id/navigation_bar"
        app:layout_anchorGravity="top|right"
        android:layout_gravity="top"
        />
    

Implmenetation of Behaviour don't hesitate to use that ! It's easy and friendly ;) (scrolling)

class BottomNavigationBehavior : CoordinatorLayout.Behavior<BottomNavigationView> {

    constructor(): super()

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)

    override fun layoutDependsOn(parent: CoordinatorLayout, child: BottomNavigationView,
                                 dependency: View): Boolean {
        if (dependency is Snackbar.SnackbarLayout) {
            updateSnackbar(child, dependency)
        }
        return super.layoutDependsOn(parent, child, dependency)
    }

    private fun updateSnackbar(child: View, snackbarLayout: Snackbar.SnackbarLayout) {
        if (snackbarLayout.layoutParams is CoordinatorLayout.LayoutParams) {
            val params = snackbarLayout.layoutParams as CoordinatorLayout.LayoutParams

            params.anchorId = child.id
            params.anchorGravity = Gravity.TOP
            params.gravity = Gravity.TOP
            snackbarLayout.layoutParams = params
        }
    }

    override fun onStartNestedScroll(
        coordinatorLayout: CoordinatorLayout,
        child: BottomNavigationView,
        directTargetChild: View,
        target: View,
        nestedScrollAxes: Int
    ): Boolean {
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
    }

    override fun onNestedPreScroll(
        coordinatorLayout: CoordinatorLayout,
        child: BottomNavigationView,
        target: View,
        dx: Int,
        dy: Int,
        consumed: IntArray
    ) {
        if (dy < 0) {
            showBottomNavigationView(child)
        } else if (dy > 0) {
            hideBottomNavigationView(child)
        }
    }

    private fun hideBottomNavigationView(view: BottomNavigationView) {
        view.animate().translationY(view.height.toFloat())
    }

    private fun showBottomNavigationView(view: BottomNavigationView) {
        view.animate().translationY(0f)
    }
}
murt
  • 3,790
  • 4
  • 37
  • 48
16

There is a simple way, with the new Material Design:

Snackbar snackbar= Snackbar.make(view, text, duration);
snackbar.setAnchorView(bottomBar);

So the Snackbar will show above the BottomNavigationView.

Ansshkki
  • 760
  • 8
  • 14
  • I did this and it works, but the Snackbar covers the fab now. – jajube Oct 31 '20 at 16:31
  • @jajube To avoid that... You should place the fab in a ```Coordinator``` layout, and the ```view``` (passed to the ```make()``` function) should be the same ```Coordinator``` layout where you placed the fab, hope this will help you :) .. tell me the result for that. – Ansshkki Nov 01 '20 at 20:47
8

Instead of programmatically manipulating CoordinatorLayout parameters, I found that you can place a GuideLine (or a View, if you are not using ConstraintLayout) above which the Snackbar may be placed. I'm using 1 here to indicate 100% meaning the bottom of the parent ConstraintLayout:

<androidx.constraintlayout.widget.Guideline
android:id="@+id/snackbar_anchor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="1" />

Use Viewbinding + this code to place the snackbar above the Guide:

Snackbar
        .make(containerView, alert.description, Snackbar.LENGTH_LONG).
        .setAnchorView(binding?.snackbarAnchor)
        .show()
mazend
  • 456
  • 1
  • 7
  • 37
Nino van Hooff
  • 3,677
  • 1
  • 36
  • 52
6

It can be done simply if the parent layout was Coordinator layout.

CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams)
                snackbar.getView().getLayoutParams();
        params.setAnchorId(R.id.navigation); //id of the bottom navigation view
        params.gravity = Gravity.TOP;
        params.anchorGravity = Gravity.TOP;
        snackbar.getView().setLayoutParams(params);
Jarin Rocks
  • 975
  • 10
  • 17
  • 2
    This works perfectly for bottom nav + FAB + hide on scroll behavior – Florian Walther Sep 28 '20 at 09:52
  • THis is the answer if you are having theme issues where the colors you just can't get right if the snackbar is showing the same line as the bottom nav. – JPM Oct 18 '22 at 22:12
6

Inspired by Gabriele Mariotti's answer here is my Kotlin solution:

Snackbar.make(show_snack_btn, "Yeeeaaay!", Snackbar.LENGTH_LONG).also {
    it.anchorView = fab
}.show()
MeLean
  • 3,092
  • 6
  • 29
  • 43
3

To achieve this you must take care that the ViewGroup you are providing to the snackbar is a CoordinatorLayout else the snackbar would not be shown above the bottom navigation menu.

sanjay kumar
  • 33
  • 1
  • 2
  • This answer is incorrect. I've passed a reference to a CoordinatorLayout as the first argument for Snackbar.make() and it does not render above the bottom view footer – Mark Lapasa Dec 31 '18 at 16:42
2

I am using the BottomNavigationView and Snackbar from the design support library version 25.3.1 on target OS kitkat, lollipop and Marshmallow. On lollipop and above Snackbar is hiding behind BottomNavigationView but in Kitkat BottomNavigationView is hidden behind Snackbar.

I tried to show the Snackbar with a different approach. When the Snackbar is shown the BottomNavigationView is translated on Y-axis(scrolled down) using the translationY property and Interpolator. Once Snackbar is gone, the BottomNavigationView appears again with the same translationY property.

Hiding the BottomNavigationView (towards bottom):

CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) bottomNavigationView.getLayoutParams();
bottomNavigationView.animate().translationY(bottomNavigationView.getHeight() + layoutParams.bottomMargin).setInterpolator(new LinearInterpolator()).start();

Showing the BottomNavigationView back on screen:

bottomNavigationView.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
Prashant
  • 1,046
  • 14
  • 21
2
Snackbar outerSnackBar = Snackbar.make(findViewById(android.R.id.content), "Your text", Snackbar.LENGTH_INDEFINITE);
View view = outerSnackBar.getView();
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)view.getLayoutParams();
params.gravity = Gravity.BOTTOM;
params.setMargins(0, 0, 0, 150);
view.setLayoutParams(params);
Nikos Hidalgo
  • 3,666
  • 9
  • 25
  • 39
0

For any of you that cannot use the CoordinatorLayout, even with the Anchor solution such as it seems in my case

It might be helpful to know you can add a listener to the Snackbar and at most (if not all) views can add layout_marginBottom at Runtime.

From my initial testing, it seems to be working quite well.

following is my code, hopes it will help anyone

public class ItemActivity extends AppCompatActivity {

    private ConstraintLayout mBottomViewConstraintLayout;
    private int mPrevBottomPadding = 0;
    private Snackbar mSnackbar;
    private Snackbar.Callback mCallback = new Snackbar.Callback() {
        @Override
        public void onShown(Snackbar sb) {
            super.onShown( sb );
            addMarginToBottomView();
        }

        @Override
        public void onDismissed(Snackbar transientBottomBar, int event) {
            super.onDismissed( transientBottomBar, event );
            initMarginToBottomView();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        //.....

        mSnackbar = Snackbar.make(
                findViewById( android.R.id.content ),
                getString( R.string.snackbar_message_undo_item_removed ),
                Snackbar.LENGTH_LONG)
                .setAction( getString( R.string.snackbar_action_undo ),
                        new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                mItemDao.undoDelete(  );
                            }
                        } )
                .addCallback( mCallback )
                .setDuration( Constants.SNACKBAR_DURATION_MS );
    }

    private void addMarginToBottomView() {
        ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) mBottomViewConstraintLayout.getLayoutParams();

        mPrevBottomPadding = layoutParams.bottomMargin;

        layoutParams.bottomMargin += mSnackbar.getView().getHeight();

        mBottomViewConstraintLayout.setLayoutParams(layoutParams);
    }

    private void initMarginToBottomView() {
        ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) mBottomViewConstraintLayout.getLayoutParams();

        layoutParams.bottomMargin = mPrevBottomPadding;

        mBottomViewConstraintLayout.setLayoutParams(layoutParams);
    }

    private void showSnackbar() {

        mSnackbar.show();

    }
epic
  • 1,333
  • 1
  • 13
  • 27