I try to implement a search bar like in google maps android app:
When the recycler view is in its initial state, the toolbar has no elevation. Only when the users starts scrolling the elevation becomes visible. And the search bar (toolbar) never collapses. Here is what I tried to replicate this:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="64dp">
<!-- content -->
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
And here you can see the result:
So the problem with my solution is, that the elevation of the toolbar is always visible. But I want it to appear only when the recycler view scrolls behind it. Is there anything from the design support library that enables such behavior as seen in the google maps app?
I am using
com.android.support:appcompat-v7:23.2.0
com.android.support:design:23.2.0
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
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!
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:
activity?.findViewById...
android:elevation="0dp"
and app:elevation="0dp"
attributes to your Toolbar or AppBarLayout so that the recycler view doesn't have elevation at the beginning.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With