Hiding the ActionBar on RecyclerView/ListView onScroll
Solution 1
Updated 6/3/2015:
Google now supports this using the CoordinatorLayout
.
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<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_done" />
</android.support.design.widget.CoordinatorLayout>
Documented here: https://developer.android.com/reference/android/support/design/widget/AppBarLayout.html
Original Answer:
Example similar to Google Play Music and Umano apps:
https://github.com/umano/AndroidSlidingUpPanel
Take a look at the code in this repository. As you slide the panel up, the ActionBar slides up as well.
From the Demo:
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
SlidingUpPanelLayout layout = (SlidingUpPanelLayout) findViewById(R.id.sliding_layout);
layout.setShadowDrawable(getResources().getDrawable(R.drawable.above_shadow));
layout.setAnchorPoint(0.3f);
layout.setPanelSlideListener(new PanelSlideListener() {
@Override
public void onPanelSlide(View panel, float slideOffset) {
Log.i(TAG, "onPanelSlide, offset " + slideOffset);
if (slideOffset < 0.2) {
if (getActionBar().isShowing()) {
getActionBar().hide();
}
} else {
if (!getActionBar().isShowing()) {
getActionBar().show();
}
}
}
@Override
public void onPanelExpanded(View panel) {
Log.i(TAG, "onPanelExpanded");
}
@Override
public void onPanelCollapsed(View panel) {
Log.i(TAG, "onPanelCollapsed");
}
@Override
public void onPanelAnchored(View panel) {
Log.i(TAG, "onPanelAnchored");
}
});
Download example here:
https://play.google.com/store/apps/details?id=com.sothree.umano
ListView - without Libraries:
I recently wanted the same functionality and this works perfectly for me:
As the user scrolls upward, the ActionBar will be hidden in order to give the user the entire screen to work work with.
As the user scrolls downward and lets go, the ActionBar will return.
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
listView.setOnScrollListener(new OnScrollListener() {
int mLastFirstVisibleItem = 0;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) { }
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (view.getId() == listView.getId()) {
final int currentFirstVisibleItem = listView.getFirstVisiblePosition();
if (currentFirstVisibleItem > mLastFirstVisibleItem) {
// getSherlockActivity().getSupportActionBar().hide();
getSupportActionBar().hide();
} else if (currentFirstVisibleItem < mLastFirstVisibleItem) {
// getSherlockActivity().getSupportActionBar().show();
getSupportActionBar().show();
}
mLastFirstVisibleItem = currentFirstVisibleItem;
}
}
});
RecyclerView - without libraries
this.mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
int mLastFirstVisibleItem = 0;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
final int currentFirstVisibleItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (currentFirstVisibleItem > this.mLastFirstVisibleItem) {
MainActivity.this.getSupportActionBar().hide();
} else if (currentFirstVisibleItem < this.mLastFirstVisibleItem) {
MainActivity.this.getSupportActionBar().show();
}
this.mLastFirstVisibleItem = currentFirstVisibleItem;
}
});
Let me know if you need anymore help!
Solution 2
You experience the flickering since by hiding/showing the ActionBar the available space for your content layout changes, which causes a relayout. With this the index of the first visible item's position changes as well (you can verify this by logging out mLastFirstVisibleItem and currentFirstVisibleItem.
You can cope with the flickering by letting the ActionBar overlay your content layout. To enable overlay mode for the action bar, you need to create a custom theme that extends an existing action bar theme and set the android:windowActionBarOverlay property to true.
With this you can eliminate the flickering but the action bar will overlay your listview's content. An easy solution to this is to set the listview's (or the root layout's) top padding to the action bar's height.
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?android:attr/actionBarSize" />
Unfortunately this will result in a constant padding at the top. A refinement of this solution is to add a header view to the list view which has the height of ?android:attr/actionBarSize (and remove the top padding set previously)
Solution 3
What you are looking for is called Quick Return pattern, applied to the Action Bar. Google IO 2014 app use exactly that. I use it in one of my apps, you can check the source code of that Google app to see how they got it. The BaseActivity class is where you have what you need, read the code and extract just that specific functionality. Enjoy coding! :)
Solution 4
I am sure that it's not the best solution. But I haven't found a better one yet. Hope it will be helpfull.
static boolean scroll_down;
...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (scroll_down) {
actionBar.hide();
} else {
actionBar.show();
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 70) {
//scroll down
scroll_down = true;
} else if (dy < -5) {
//scroll up
scroll_down = false;
}
}
});
Solution 5
I have a thought that it is a good idea.
listView.setOnScrollListener(new OnScrollListener() {
int mLastFirstVisibleItem = 0;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_FLING:
if (view.getId()==lv_searchresult_results.getId()){
final int currentFirstVisibleItem=lv_searchresult_results.getFirstVisiblePosition();
if(currentFirstVisibleItem>mLastFirstVisibleItem){
ObjectAnimator.ofFloat(toolbar, "translationY", -toolbar.getHeight()).start();
else if(currentFirstVisibleItem<(mLastFirstVisibleItem)){
ObjectAnimator.ofFloat(toolbar, "translationY", 0).start();
}
mLastFirstVisibleItem= currentFirstVisibleItem;
}
if(lv_searchresult_results.getLastVisiblePosition() == myAdapter.getListMap().size()){
if(myAdapter.getListMap().size() < allRows&&!upLoading){
}
}
break;
case OnScrollListener.SCROLL_STATE_IDLE:
if (view.getFirstVisiblePosition()<=1){
ObjectAnimator.ofFloat(toolbar, "translationY", 0).start();
}
if(lv_searchresult_results.getLastVisiblePosition() == myAdapter.getListMap().size()){
if(myAdapter.getListMap().size() < allRows&&!upLoading){
}
}
break;
Related videos on Youtube
Graykos
Updated on August 26, 2020Comments
-
Graykos over 3 years
In my application I got an activity with some kind of actionbar at the top and the listview below it. What I want to do - is to scroll it UP with the list, so it hides and then, when the list is being scrolled down - it should scroll down with the list, like it was just over the upper screen border. how can i achieve this functionality?
-
Nirav Ranpara over 11 years
-
Jared Burrows about 9 years@Graykos, can you choose a correct answer?
-
-
Ivan Morgillo about 10 yearsI like it. It seems to work, but there is an ugly flickering of the actionbar when it hides :-(
-
Jared Burrows about 10 years@hamen Yes, I have experienced the same issue. If you can find a better solution please let me know!
-
Dandre Allison almost 10 years@JaredBurrows ne'mi's answer explains what's causing the flickering and how to avoid it. I recommend adding a "header" view with the Action Bar padding.
-
djdance over 9 yearsmy two androids >4.2 works without flickering, thanks
-
Jared Burrows over 9 yearsIf you are using ActionBarSherlock or AppCompat, use android:paddingTop="?attr/actionBarSize"
-
Ivan Morgillo over 9 yearsI used your "A refinement" for my GridView. I forked and fixed a tiny lib. You can find it here: github.com/alter-ego/GridViewHeader
-
Piyush Kukadiya over 9 yearsBut how to do it with webview scroll ?
-
Georgy Gobozov over 9 yearsThat's works fine with plain listview ( listview header + acrion bar overlay) what about if my listview inside viewpager fragment with pagerrabstips? Iguess i need something like header view but for viewpager. Any suggestions?
-
Jared Burrows over 9 years@GeorgyGobozov I am not sure. I have used this method before with tabs just to hide the actionbar and keep the tabs similar to the google play store.
-
Georgy Gobozov over 9 years@Jared Burrows did you use actionbar overlay and padding for this? (like in answer below) Or you just called actionBar.hide()?
-
Jared Burrows over 9 years@GeorgyGobozov Yes, <item name="windowActionBarOverlay">true</item>.
-
Diego Palomar over 9 years@PiyushKukadiya the implementation is almost the same, just define a scroll listener for the Webview, here you have a full axample about how to do it: stackoverflow.com/questions/14752523/…
-
CQM over 9 yearsI did this but now I can't make the layout larger when the actionbar is hidden, how do I smoothly make the layout fit the space the actionbar once too
-
jaibatrik about 9 yearsEven I arrived at the same solution, but can't avoid flickering. Have you been able to remove the flickering caused by the show / hide?
-
Alexey about 9 yearsMy last version was: if (dy > 40) { scroll_down = true; } else if (dy < -5) { scroll_down = false; }
-
SammyT about 9 yearsI used the plain listview solution, but hid my navigation tabs instead, with: if (currentFirstVisibleItem > mLastFirstVisibleItem) { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); } else if (currentFirstVisibleItem < mLastFirstVisibleItem) { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); } Not sure if there is a better way to do it, but it works nicely no flickering.
-
Jared Burrows about 9 years@SammyT Those tabs are deprecated.
-
SammyT about 9 yearsThanks @JaredBurrows, I will add it to my todo list to replace them. The google training docs look like they haven't been updated yet. developer.android.com/training/implementing-navigation/…
-
splinter123 almost 9 yearswhat about if we have a SlidingTabLayout with a ViewPager? By using Actionbar Overlay we would hide the tabs!
-
Jared Burrows almost 9 years@splinter123 Yeah, post a solution for that!
-
splinter123 almost 9 years@JaredBurrows still can't find an easy one. I thought it would be something standard, since it's the play app and many others...
-
Jared Burrows almost 9 years@splinter123 Google always tends to add features in their own apps and the do not share their "special" tricks. This is why people create their own open source libraries on Github.
-
Dark Leonhart almost 9 yearsI've tried that cheesesquare sample, but at the first layout (toolbar with tablayout) the toolbar doesn't hide when I scrolled up the list..someone know why?
-
Jared Burrows almost 9 years@DarkLeonhart I would need to see your code. The toolbar needs to be in the
AppLayout
and in theCoordinatorLayout
. -
Dark Leonhart almost 9 years@JaredBurrows Thanks, it worked already. The mistake is I'm still using recyclerview.jar from previous version (I'm still using Eclipse). The jar needs to be the latest version (v22).
-
Admin almost 9 yearsIs there any way we can use the coordinator layout to also hide a bottom bar or the FAB ?
-
Jared Burrows almost 9 years@Lupsaa Have you tried to use the layout with
app:layout_scrollFlags="scroll|enterAlways"
or something similar for your needs? -
Jared Burrows almost 9 years@Dalvikvm I posted that link in my answer.
-
Vihaan Verma almost 9 yearshow to make it work with StaggeredGridLayoutManager ?
-
Jared Burrows almost 9 years@VihaanVerma Did you even try anything? The code should be the same.
-
Vihaan Verma almost 9 years@JaredBurrows I did but with staggered gird you an array of positions. The view on the first position is pretty big so the actionbar in my case won't hide till the very first time is up for recycling.
-
Jared Burrows almost 9 yearsI am not sure. I have not used the
StaggeredGridLayoutManager
-
Paolo Rotolo over 8 yearsPlease use
addOnScrollListener(OnScrollListener)
on RecyclerView becausesetOnScrollListener
is deprecated. -
yat0 over 8 yearsJared, I'm using the
Coordinator Layout
and the rest inside just like you use in the first code example, and I've a fragment with aListView
insideSwipe Refresh Layout
inside aFrame Layout
.. But the toolbar is not hiding when I scroll down this list.. what could the error be? -
Ishaan over 7 years@JaredBurrows: I tried the latest version of your answer but somehow it does not work with ListView scrolling. Any ideas?
-
Jared Burrows over 7 years@Ishaan I wrote this a while ago. Don't use
ListView
, useRecyclerView
. -
Ishaan over 7 yearsThanks @JaredBurrows. The reason why I am using ListView is because I need fast scrolling which I am unable to achieve on recycler view. I referred github.com/danoz73/RecyclerViewFastScroller but it is very buggy.
-
Shailendra Madda over 5 yearsdid anyone remove that flickering effect when we scroll slowly?
-
Vikas Acharya almost 4 yearsjust animate it for smooth ness