在RecyclerView列表滚动的时候显示或者隐藏Toolbar

先看一下效果:

本文将讲解如何实现类似于Google+应用中,当列表滚动的时候,ToolBar(以及悬浮操作按钮)的显示与隐藏(向下滚动隐藏,向上滚动显示),这种效果在Material Design 清单中有提到:

“在合适的地方,当列表向下滚动,app bar可以退出屏幕,以便为内容区域留下更多的空间;而当列表向上滚动回来的时候,app bar又重新显示出来”。

注:这里的向下滚动是指滚动到下面查看更多内容,相对应的手势操作其实是往上。同理向上滚动是指查看前面的内容,而手势其实是向下。

虽然此文我们将使用RecyclerView作为列表,但是这种实现方式适用于任何可以滚动的容器(某些情况下也许要稍微多做点工作,比如listview)。我想到了两种实现的方式:

  1. 在列表的上面加个padding。
  2. 为列表加个header。

我打算只写出第二种实现方式,因为有很多人询问关于如何给RecyclerView加上header的问题,因此借着这个机会就一起讲了。但是我也会非常简单的描述一下第一种实现方法。

开始

首先添加必要的库

 1 dependencies {
 2     compile fileTree(include: [‘*.jar‘], dir: ‘libs‘)
 3     androidTestCompile(‘com.android.support.test.espresso:espresso-core:2.2.2‘, {
 4         exclude group: ‘com.android.support‘, module: ‘support-annotations‘
 5     })
 6     compile ‘com.android.support:appcompat-v7:25.3.1‘
 7     compile ‘com.android.support.constraint:constraint-layout:1.0.2‘
 8     testCompile ‘junit:junit:4.12‘
 9     compile ‘com.android.support:design:26.0.0-alpha1‘
10     compile ‘com.android.support:cardview-v7:26.0.0-alpha1‘
11     compile ‘com.jakewharton:butterknife:8.7.0‘
12     compile ‘com.jakewharton:butterknife-compiler:8.7.0‘
13     compile ‘de.greenrobot:eventbus:3.0.0-beta1‘
14 }

创建activity的布局:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:tools="http://schemas.android.com/tools"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     android:orientation="vertical"
 7     tools:context="recycler.huolongluo.hideonscrollexample.MainActivity">
 8
 9     <android.support.v7.widget.Toolbar
10         android:id="@+id/toolbar"
11         android:layout_width="match_parent"
12         android:layout_height="?attr/actionBarSize"
13         android:background="@color/colorPrimaryDark"
14         tools:layout_editor_absoluteX="8dp"
15         tools:layout_editor_absoluteY="0dp" />
16
17     <android.support.v7.widget.RecyclerView
18         android:id="@+id/rv_content"
19         android:layout_width="match_parent"
20         android:layout_height="match_parent" />
21
22 </LinearLayout>

下面转向MainActivity的代码:

 1 package recycler.huolongluo.hideonscrollexample;
 2
 3 import android.os.Bundle;
 4 import android.support.v7.app.AppCompatActivity;
 5 import android.support.v7.widget.LinearLayoutManager;
 6 import android.support.v7.widget.RecyclerView;
 7 import android.support.v7.widget.Toolbar;
 8 import android.util.Log;
 9 import android.view.View;
10 import android.view.animation.AccelerateInterpolator;
11 import android.view.animation.DecelerateInterpolator;
12
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import butterknife.BindView;
17 import butterknife.ButterKnife;
18
19 public class MainActivity extends AppCompatActivity {
20     private static final String TAG = "MainActivity";
21
22     @BindView(R.id.toolbar)
23     Toolbar toolbar;
24     @BindView(R.id.rv_content)
25     RecyclerView rv_content;
26
27     private List<String> datas;
28     private MyAdapter myAdapter;
29
30     @Override
31     protected void onCreate(Bundle savedInstanceState) {
32         super.onCreate(savedInstanceState);
33         setContentView(R.layout.activity_main);
34         ButterKnife.bind(this);
35         initData();
36         initView();
37     }
38
39     private void initView() {
40         myAdapter = new MyAdapter(this, datas);
41         rv_content.setLayoutManager(new LinearLayoutManager(this));
42         rv_content.setAdapter(myAdapter);
43
44         /**
45          * 实现RecyclerView上下滑动的显示和隐藏
46          * */
47         rv_content.addOnScrollListener(new RecyclerView.OnScrollListener() {
48             private static final int HIDE_THRESHOLD = 20;
49             private int scrolledDistance = 0;
50             private boolean controlsVisible = true;
51
52             @Override
53             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
54                 super.onScrolled(recyclerView, dx, dy);
55                 Log.e(TAG, "onScrolled dy: " + dy);
56                 Log.e(TAG, "onScrolled dx: " + dx);
57                 Log.e(TAG, "-------------------- onScrolled: --------------------");
58                 if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
59                     // TODO: 2017/7/16 0016 隐藏toolbar
60                     hideViews();
61 //                    toolbar.setVisibility(View.GONE);
62                     controlsVisible = false;
63                     scrolledDistance = 0;
64                 } else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
65                     // TODO: 2017/7/16 0016 显示toolbar
66                     showViews();
67 //                    toolbar.setVisibility(View.VISIBLE);
68                     controlsVisible = true;
69                     scrolledDistance = 0;
70                 }
71                 if ((controlsVisible && dy > 0) || (!controlsVisible && dy < 0)) {
72                     scrolledDistance += dy;
73                 }
74             }
75         });
76     }
77
78     private void hideViews() {
79         toolbar.animate().translationY(-toolbar.getHeight()).setInterpolator(new AccelerateInterpolator(2));
80     }
81
82     private void showViews() {
83         toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
84     }
85
86     private void initData() {
87         if (datas == null) {
88             datas = new ArrayList<>();
89         }
90         for (int i = 0; i < 20; i++) {
91             datas.add("Item " + i);
92         }
93     }
94 }

如你所见,这是一个很小的类,只实现了onCreate,做了如下几件事情:

1.初始化Toolbar

2.初始化数据源

3.初始化RecyclerView

现在来看下适配器Adapter的写法,适配器前,我先把需要用到的两个不同布局的item画出来了。一个item作为RecyclerView的头部,一个item作为RecyclerView的内容。

item_head.xml:

1 <?xml version="1.0" encoding="utf-8"?>
2 <View xmlns:android="http://schemas.android.com/apk/res/android"
3     android:id="@+id/view_head"
4     android:layout_width="match_parent"
5     android:layout_height="?attr/actionBarSize"
6     android:orientation="vertical" />

item_content.xml:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:app="http://schemas.android.com/apk/res-auto"
 4     android:layout_width="match_parent"
 5     android:layout_height="wrap_content"
 6     android:layout_gravity="center"
 7     android:layout_margin="20dp"
 8     android:orientation="vertical"
 9     app:cardCornerRadius="10dp">
10
11     <TextView
12         android:id="@+id/tv_title"
13         android:layout_width="match_parent"
14         android:layout_height="wrap_content"
15         android:padding="20dp"
16         android:text="Item "
17         android:textSize="20sp" />
18
19 </android.support.v7.widget.CardView>

布局很简单,只需注意其高度要和Toolbar一致。

接下来我们看适配器MyAdapter.java:

 1 package recycler.huolongluo.hideonscrollexample;
 2
 3 import android.content.Context;
 4 import android.support.v7.widget.RecyclerView;
 5 import android.view.LayoutInflater;
 6 import android.view.View;
 7 import android.view.ViewGroup;
 8 import android.widget.TextView;
 9
10 import java.util.List;
11
12 /**
13  * Created by Administrator on 2017/7/16 0016.
14  */
15
16 class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
17
18     private Context context;
19     private List<String> datas;
20     private LayoutInflater inflater;
21
22     public MyAdapter(Context context, List<String> datas) {
23         this.context = context;
24         this.datas = datas;
25         inflater = LayoutInflater.from(context);
26     }
27
28     @Override
29     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
30         if (viewType == 0) {
31             return new ViewHolderHead(inflater.inflate(R.layout.item_head, parent, false));
32         } else {
33             return new ViewHolderContent(inflater.inflate(R.layout.item_content, parent, false));
34         }
35     }
36
37     @Override
38     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
39         if (holder instanceof ViewHolderContent) {
40             ((ViewHolderContent) holder).tv_title.setText(datas.get(position - 1));
41         }
42     }
43
44     @Override
45     public int getItemCount() {
46         return datas.size() + 1;
47     }
48
49     @Override
50     public int getItemViewType(int position) {
51         if (position == 0) {
52             return 0;
53         } else {
54             return 1;
55         }
56     }
57
58     public class ViewHolderContent extends RecyclerView.ViewHolder {
59         private TextView tv_title;
60
61         public ViewHolderContent(View itemView) {
62             super(itemView);
63             tv_title = (TextView) itemView.findViewById(R.id.tv_title);
64         }
65     }
66
67     public class ViewHolderHead extends RecyclerView.ViewHolder {
68         private View view_head;
69
70         public ViewHolderHead(View itemView) {
71             super(itemView);
72             view_head = (View) itemView.findViewById(R.id.view_head);
73         }
74     }
75 }

到这里就已经全部写完了,

下面是关于上面代码的解释:

1.需要定义Recycler显示的item的类型。RecyclerView是一个非常灵活的控件,当某些item的布局和其他item有区别的时候,我们一般要用到item类型。这也正是我们这里需要的-第一个item是header,不同于其他item。

2.我们需要告诉Recycler,item想要显示的类型。getItemViewType方法将根据position返回一个item的类型(int类型,具体值由你根据项目需求自己定义)。

3.需要修改onCreateViewHolder()onBindViewHolder()方法,在item类型为非0的时候绑定或者返回一个普通item,在item类型为0的时候返回或绑定一个header item。

4.需要修改getItemCount()-在原有的基数上+1因为多了个header

正如你所看到的,所有关键代码都在一个onScrolled()方法中。其dx, dy参数分别是横向和纵向的滚动距离,准确是的是两个滚动事件之间的偏移量,而不是总的滚动距离。

基本的思路如下:

1.计算出滚动的总距离(deltas相加),但是只在Toolbar隐藏且上滚或者Toolbar未隐藏且下滚的时候,因为我们只关心这两种情况。

1 if((controlsVisible && dy>0) || (!controlsVisible && dy<0)) {
2     scrolledDistance += dy;
3 }

2.如果总的滚动距离超多了一定值(这个值取决于你自己的设定,越大,需要滑动的距离越长才能显示或者隐藏),我们就根据其方向显示或者隐藏Toolbar(dy>0意味着下滚,dy<0意味着上滚)。

1 if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
2     onHide();
3     controlsVisible = false;
4     scrolledDistance = 0;
5 } else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
6     onShow();
7     controlsVisible = true;
8     scrolledDistance = 0;
9 }

最后,toolbar的显示和隐藏是通过动画来控制的,一句搞定。

基本上是正确的,但是还有点bug-如果你的滑动距离的触发值太小,在隐藏Toolbar的时候会在列表的顶部留下一段空白区域(最开始,随着滚动空白区域会消失),幸好解决起来也很简单。只需检测第一个item是否可见,只有当不可见的时候才执行上面的逻辑。

于是,在重写的addOnScrollListener方法里面,最新的代码是这样的:

 1 /**
 2          * 实现RecyclerView上下滑动的显示和隐藏
 3          * */
 4         rv_content.addOnScrollListener(new RecyclerView.OnScrollListener() {
 5             private static final int HIDE_THRESHOLD = 20;
 6             private int scrolledDistance = 0;
 7             private boolean controlsVisible = true;
 8
 9             int firstVisibleItem = ((LinearLayoutManager) rv_content.getLayoutManager()).findFirstVisibleItemPosition();
10
11             @Override
12             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
13                 super.onScrolled(recyclerView, dx, dy);
14                 Log.e(TAG, "onScrolled dy: " + dy);
15                 Log.e(TAG, "onScrolled dx: " + dx);
16                 Log.e(TAG, "-------------------- onScrolled: --------------------");
17                 if (firstVisibleItem == 0) {
18                     if (!controlsVisible) {
19                         showViews();
20                         controlsVisible = true;
21                     }
22                 } else {
23                     if (scrolledDistance > HIDE_THRESHOLD && controlsVisible) {
24                         // TODO: 2017/7/16 0016 隐藏toolbar
25                         hideViews();
26 //                    toolbar.setVisibility(View.GONE);
27                         controlsVisible = false;
28                         scrolledDistance = 0;
29                     } else if (scrolledDistance < -HIDE_THRESHOLD && !controlsVisible) {
30                         // TODO: 2017/7/16 0016 显示toolbar
31                         showViews();
32 //                    toolbar.setVisibility(View.VISIBLE);
33                         controlsVisible = true;
34                         scrolledDistance = 0;
35                     }
36                 }
37                 if ((controlsVisible && dy > 0) || (!controlsVisible && dy < 0)) {
38                     scrolledDistance += dy;
39                 }
40             }
41         });
时间: 2024-10-12 09:08:08

在RecyclerView列表滚动的时候显示或者隐藏Toolbar的相关文章

jquery中index实现列表中元素的显示和隐藏

var last = ""; var lastIndex = ""; $(".ykyw_list2 ul li").click(function(){ var current = $(this); var currentIndex = $(this).index()+1; if (currentIndex != lastIndex && lastIndex != "") { last.removeClass(&

Android下拉上滑显示与隐藏Toolbar另一种实现

public abstract class RecyclerViewScrollListener extends RecyclerView.OnScrollListener { private static final int SCROLL_DISTANCE = 50; private int totalScrollDistance; private boolean isShow = true; @Override public void onScrollStateChanged(Recycle

ListView或者RecyclerView滚动时隐藏Toolbar (1)

原文链接 : How to hide/show Toolbar when list is scroling (part 1) 译者 : chaossss 校对者: 这里校对者的github用户名 状态 : 校对中 今天我打算写一篇博文给大家介绍Google+ App的一个酷炫效果--向上/向下滚动ListView/RecyclerView时,Toolbar和FAB(屏幕右下方的小按钮)会隐藏/出现.这个效果也被Google视为符合 Material Design 规范的效果哦,详情参见: Mat

RecyclerView的滚动事件OnScrollListener研究

(1)滚动事件分类 列表的滚动一般分为两种: 1.手指按下 -> 手指拖拽列表移动 -> 手指停止拖拽 -> 抬起手指 2.手指按下 -> 手指快速拖拽后抬起手指 -> 列表继续滚动 -> 停止滚动 上面的过程的状态变化如下: 1.静止 -> 被迫拖拽移动 -> 静止 2.静止 -> 被迫拖拽移动 -> 自己滚动 -> 静止 (2)监听RecyclerView的滚动 有两种方式可以监听滚动事件: 1.setOnScrollListener(

手机端优化列表滚动性能——分页加载

现在在手机端列表滚动随处可见,拿现在大家都在用的微信来说,朋友圈就是一个列表,和好友发信息界面就是一个列表. 如果列表长度不长,比如你微信朋友圈一共就只有两三个人分享过他们的动态,那没问题,如果你是大屏手机估计还不用滚动就已经看完了. 但那有可能吗?有也只是少数.我们现在每天刷朋友圈那都是几屏几屏这样刷的. 我们设定一屏有五个人分享的动态,每一个动态里面有5个dom节点,即一屏有25个dom节点. 我们开始刷朋友圈,才看了四屏就已经100个节点了,再往下刷成百甚至上千个dom节点都出来了,那你手

页面滚动显示或隐藏元素Headroom.js插件帮助你实现滚动效果

Headroom.js 是什么? Headroom.js 是一个轻量级.高性能的JS小工具(不依赖任何工具库!),它能在页面滚动时做出响应.此页面顶部的导航条就是一个鲜活的案例,当页面向下滚动时,导航条消失,当页面向上滚动时,导航条就出现了. Headroom.js 有什么用? 固定页头(导航条)可以方便用户在各个页面之间切换.但是这也会带来些问题…本文原创博客地址:http://www.cnblogs.com/unofficial官网地址:www.pushself.com) 大屏幕一般都是宽度

点击显示或隐藏列表

素材: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Cont

基于jQuery左侧小图滚动右侧大图显示代码

今天给大家分享一款 jQuery左侧小图滚动右侧大图显示代码是一款基于jQuery实现的左侧滚动图片点击大图查看效果代码.该实例适用浏览器:IE8.360.FireFox.Chrome.Safari.Opera.傲游.搜狗.世界之窗.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div class="caseImg03 w1002"> <div class="slideCase03"> <div class=&q

自动滚动,自动显示下一个图像

效果图: 每隔1s会自动切换图像和小白点. 工程图: 代码: RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController : UIViewController <UIScrollViewDelegate,UIPageViewControllerDelegate> { UIScrollView *imageScrollView; UIPageControl *pageControl; NSInt