滚动RecyclerView/ListView时隐藏ActionBar

46

在我的应用程序中,我有一个带有某种操作栏的活动,并且下面是列表视图。我想要做的是-随着列表向上滚动,隐藏它,然后当列表向下滚动时-它应该随着列表向下滚动,就像它只是在屏幕顶部边缘之上。如何实现这个功能?


https://dev59.com/cGbWa4cB1Zd3GeqPTiOo - Nirav Ranpara
1
@Graykos,你能选择一个正确的答案吗? - Jared Burrows
7个回答

77

更新于2015年6月3日:

现在,Google支持使用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>

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

文档:https://developer.android.com/reference/android/support/design/widget/AppBarLayout.html

原始答案:

类似于Google Play音乐和Umano应用的示例:

https://github.com/umano/AndroidSlidingUpPanel

请查看此代码库中的代码。当您向上滑动面板时,操作栏也会向上滑动。 来自演示:
   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");

        }
    });

在此下载示例:

https://play.google.com/store/apps/details?id=com.sothree.umano

enter image description here

ListView - 无需使用库:

最近我也想要相同的功能,这个方法对我来说非常完美:

当用户向上滚动时,ActionBar 将被隐藏,以便为用户提供整个屏幕进行操作。

当用户向下滚动并放开时,ActionBar 将返回。

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 - 不使用库

    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;
        }
    });

如果您需要更多帮助,请告诉我!


我喜欢它。看起来它可以工作,但是当操作栏隐藏时会出现丑陋的闪烁 :-( - Ivan Morgillo
1
我的两个安卓版本大于4.2的设备没有闪烁问题,谢谢。 - djdance
3
使用普通的列表视图(列表视图标题 + 操作栏覆盖)效果不错,但是如果我的列表视图位于带有PagerTabStrip的ViewPager片段中,该怎么办?我想我需要类似标题视图的东西,但是针对ViewPager。有什么建议吗? - Georgy Gobozov
1
@SammyT,那些选项卡已经过时了。 - Jared Burrows
1
@JaredBurrows 谢谢,已经成功了。我的错误是我仍在使用以前版本(我仍在使用Eclipse)的recyclerview.jar。这个jar需要是最新版本(v22)。 - Dark Leonhart
显示剩余25条评论

11

当您隐藏/显示ActionBar时,可用于内容布局的空间会发生更改,这会导致重新布局,从而导致闪烁。因此,第一个可见项的位置索引也会随之更改(您可以通过记录 mLastFirstVisibleItemcurrentFirstVisibleItem 进行验证)。

您可以通过让ActionBar覆盖内容布局来解决闪烁问题。要为ActionBar启用覆盖模式,您需要创建一个扩展现有ActionBar主题的自定义主题,并将 android:windowActionBarOverlay 属性设置为true

这样就可以消除闪烁,但ActionBar将覆盖列表视图的内容。解决这个问题的简单方法是将列表视图(或根布局)的顶部填充设置为ActionBar的高度。

<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" />

不幸的是,这将导致顶部出现常量间距。对此解决方案的改进是向列表视图添加一个标题视图,其高度为?android:attr/actionBarSize (并删除先前设置的顶部间距)。


2
如果您正在使用ActionBarSherlock或AppCompat,请使用android:paddingTop="?attr/actionBarSize"。 - Jared Burrows
我在我的GridView中使用了你的“一个改进”。我进行了分支并修复了一个微小的库。你可以在这里找到它:https://github.com/alter-ego/GridViewHeader - Ivan Morgillo

8
你所需要的是称为快速返回模式的内容,应用于操作栏。Google IO 2014 应用正好使用了这个功能。我在我的一个应用程序中使用它,你可以查看那个 Google 应用程序的源代码,看看他们是如何实现的。BaseActivity类是你所需的位置,阅读代码并提取特定功能即可。享受编程吧! :)

但是如何使用Webview滚动来实现呢? - Piyush Kukadiya
@PiyushKukadiya 实现方式几乎相同,只需为Webview定义一个滚动监听器,这里有一个完整的示例:https://dev59.com/TWUq5IYBdhLWcg3wJNHg - Diego Palomar
我已经完成了这个任务,但是当ActionBar被隐藏时,我无法使布局变大。请问如何让布局平滑地适应ActionBar原来的空间? - CQM

3

我确定这不是最优的解决方案。但是我还没有找到更好的。希望这会有所帮助。

    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;
            }
        }
    });

2
虽然我也找到了相同的解决方案,但仍无法避免闪烁。你能否成功消除由显示/隐藏引起的闪烁? - jaibatrik
我的最后一个版本是:如果(dy>40){ scroll_down = true; } else if(dy < -5){ scroll_down = false; } - Alexey
有人解决了我们缓慢滚动时的闪烁效果吗? - Shailendra Madda
只是为了流畅而使它动起来。 - Vikas Acharya

1

我认为这是一个好主意。

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;

0

我认为它会很好地工作......

listView.setOnScrollListener(new OnScrollListener() {
            int mLastFirstVisibleItem = listView.getLastVisiblePosition();
            boolean isScrolling = false;

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if(scrollState == 0)
                    isScrolling = false;
                else if(scrollState == 1)
                    isScrolling = true;
                else if(scrollState == 2)
                    isScrolling = true;     
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {    
                if (view.getId() == myTeamListView.getId()) {

                    if(isScrolling) {
                    final int currentFirstVisibleItem = myTeamListView.getLastVisiblePosition();

                    if (currentFirstVisibleItem > mLastFirstVisibleItem) {                      
                        ((AppCompatActivity)getActivity()).getSupportActionBar().hide();
                    } else if (currentFirstVisibleItem < mLastFirstVisibleItem) {                       
                        ((AppCompatActivity)getActivity()).getSupportActionBar().show();                    
                    }
                    mLastFirstVisibleItem = currentFirstVisibleItem;
                    }
                }
            }
        });

0
如果您正在使用CoordinatorLayout,这里有一个技巧。
<android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    app:layout_scrollFlags="scroll|enterAlways" />

app:layout_scrollFlags="scroll|enterAlways" 这一行代码将会使得我们的工具栏在用户向下滚动列表时从屏幕上滚出,而当用户开始向上滚动时,工具栏又会重新显示出来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接