Hiding the ActionBar on RecyclerView/ListView onScroll

51,619

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>

Source: https://github.com/chrisbanes/cheesesquare/blob/master/app/src/main/res/layout/include_list_viewpager.xml

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

enter image description here

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;
Share:
51,619

Related videos on Youtube

Graykos
Author by

Graykos

Updated on August 26, 2020

Comments

  • Graykos
    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?

  • Ivan Morgillo
    Ivan Morgillo about 10 years
    I like it. It seems to work, but there is an ugly flickering of the actionbar when it hides :-(
  • Jared Burrows
    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
    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
    djdance over 9 years
    my two androids >4.2 works without flickering, thanks
  • Jared Burrows
    Jared Burrows over 9 years
    If you are using ActionBarSherlock or AppCompat, use android:paddingTop="?attr/actionBarSize"
  • Ivan Morgillo
    Ivan Morgillo over 9 years
    I 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
    Piyush Kukadiya over 9 years
    But how to do it with webview scroll ?
  • Georgy Gobozov
    Georgy Gobozov over 9 years
    That'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
    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
    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
    Jared Burrows over 9 years
    @GeorgyGobozov Yes, <item name="windowActionBarOverlay">true</item>.
  • Diego Palomar
    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
    CQM over 9 years
    I 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
    jaibatrik about 9 years
    Even 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
    Alexey about 9 years
    My last version was: if (dy > 40) { scroll_down = true; } else if (dy < -5) { scroll_down = false; }
  • SammyT
    SammyT about 9 years
    I used the plain listview solution, but hid my navigation tabs instead, with: if (currentFirstVisibleItem > mLastFirstVisibleItem) { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDA‌​RD); } 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
    Jared Burrows about 9 years
    @SammyT Those tabs are deprecated.
  • SammyT
    SammyT about 9 years
    Thanks @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
    splinter123 almost 9 years
    what about if we have a SlidingTabLayout with a ViewPager? By using Actionbar Overlay we would hide the tabs!
  • Jared Burrows
    Jared Burrows almost 9 years
    @splinter123 Yeah, post a solution for that!
  • splinter123
    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
    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
    Dark Leonhart almost 9 years
    I'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
    Jared Burrows almost 9 years
    @DarkLeonhart I would need to see your code. The toolbar needs to be in the AppLayout and in the CoordinatorLayout.
  • Dark Leonhart
    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
    Admin almost 9 years
    Is there any way we can use the coordinator layout to also hide a bottom bar or the FAB ?
  • Jared Burrows
    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
    Jared Burrows almost 9 years
    @Dalvikvm I posted that link in my answer.
  • Vihaan Verma
    Vihaan Verma almost 9 years
    how to make it work with StaggeredGridLayoutManager ?
  • Jared Burrows
    Jared Burrows almost 9 years
    @VihaanVerma Did you even try anything? The code should be the same.
  • Vihaan Verma
    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
    Jared Burrows almost 9 years
    I am not sure. I have not used the StaggeredGridLayoutManager
  • Paolo Rotolo
    Paolo Rotolo over 8 years
    Please use addOnScrollListener(OnScrollListener) on RecyclerView because setOnScrollListener is deprecated.
  • yat0
    yat0 over 8 years
    Jared, 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 a ListView inside Swipe Refresh Layout inside a Frame Layout.. But the toolbar is not hiding when I scroll down this list.. what could the error be?
  • Ishaan
    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
    Jared Burrows over 7 years
    @Ishaan I wrote this a while ago. Don't use ListView, use RecyclerView.
  • Ishaan
    Ishaan over 7 years
    Thanks @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
    Shailendra Madda over 5 years
    did anyone remove that flickering effect when we scroll slowly?
  • Vikas Acharya
    Vikas Acharya almost 4 years
    just animate it for smooth ness