Android toolbar elevation when scrolling
The accepted answer is outdated. Now there is inbuilt functionality to do this. I am pasting the whole layout code so it will help you to understand.
You just need to use CoordinatorLayout
with AppBarLayout
. This design pattern is called Lift On Scroll and can be implemented by setting app:liftOnScroll="true"
on your AppBarLayout
.
Note: the liftOnScroll
attribute requires that you apply the @string/appbar_scrolling_view_behavior
layout_behavior to your scrolling view (e.g., NestedScrollView
, RecyclerView
, etc.).
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:background="@color/default_background">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScroll="true">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/default_background" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/appbar"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:orientation="vertical" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Refered this documentation https://github.com/material-components/material-components-android/blob/master/docs/components/AppBarLayout.md
This is a good question but none of the existing answers are good enough. Calling getTop()
is absolutely not recommended as it's very unreliable. If you look at newer versions of Google apps that follow Material Design Refresh (2018) guidelines, they hide the elevation at the beginning and immediately add it as user scrolls down and hide it again as user scrolls and reaches the top again.
I managed to achieve the same effect using the following:
val toolbar: android.support.v7.widget.Toolbar? = activity?.findViewById(R.id.toolbar);
recyclerView?.addOnScrollListener(object: RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy);
if(toolbar == null) {
return;
}
if(!recyclerView.canScrollVertically(-1)) {
// we have reached the top of the list
toolbar.elevation = 0f
} else {
// we are not at the top yet
toolbar.elevation = 50f
}
}
});
This works perfectly with vertical recycler views (even with tab view or other recycler views inside them);
A couple of important notes:
- Here I'm doing this inside a fragment hence
activity?.findViewById...
- If your Toolbar is nested inside an AppBarLayout, then instead of applying elevation to Toolbar, you should apply it to the AppBarLayout.
- You should add
android:elevation="0dp"
andapp:elevation="0dp"
attributes to your Toolbar or AppBarLayout so that the recycler view doesn't have elevation at the beginning.
EDIT As pointed out in the comments, my answer is now outdated, see https://stackoverflow.com/a/58272283/4291272
Whether you are using a CoordinatorLayout
or not, a RecyclerView.OnScrollListener
seems like the right way to go as far as the elevation is concerned. However, from my experience recyclerview.getChild(0).getTop()
is not reliable and should not be used for determining the scrolling state. Instead, this is what's working:
private static final int SCROLL_DIRECTION_UP = -1;
// ...
// Put this into your RecyclerView.OnScrollListener > onScrolled() method
if (recyclerview.canScrollVertically(SCROLL_DIRECTION_UP)) {
// Remove elevation
toolbar.setElevation(0f);
} else {
// Show elevation
toolbar.setElevation(50f);
}
Be sure to assign a LayoutManager
to your RecyclerView
or the call of canScrollVertically may cause a crash!
I have a RecyclerView in my fragment. I could achieve similar effect using code below:
It is not the Smartest way and you can wait for better answers.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Initial Elevation
final Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
if(toolbar!= null)
toolbar.setElevation(0);
// get initial position
final int initialTopPosition = mRecyclerView.getTop();
// Set a listener to scroll view
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if(toolbar!= null && mRecyclerView.getChildAt(0).getTop() < initialTopPosition ) {
toolbar.setElevation(50);
} else {
toolbar.setElevation(0);
}
}
});
}