1
前言
在一些特定的场景下,如照片的浏览,卡片列表滑动浏览,我们希望当滑动停止时可以将当前的照片或者卡片停留在屏幕中央,以吸引用户的焦点。在 Android 中,我们可以使用RecyclerView + Snaphelper 来实现,SnapHelper 旨在支持 RecyclerView 的对齐方式,也就是通过计算对齐 RecyclerView 中 TargetView 的指定点或者容器中的任何像素点(包括前面说的显示在屏幕中央)。本篇文章将详细介绍 SnapHelper 的相关知识点。本文目录如下:
2
SnapHelper 介绍
Google 在 Android 24.2.0 的 support 包中添加了 SnapHelper,SnapHelper 是对RecyclerView 的拓展,结合 RecyclerView 使用,能很方便的做出一些炫酷的效果。SnapHelper 到底有什么功能呢?SnapHelper 旨在支持 RecyclerView 的对齐方式,也就是通过计算对齐 RecyclerView 中 TargetView 的指定点或者容器中的任何像素点。,可能有点不好理解,看了后文的效果和原理分析就好理解了。看一下文档介绍:
SnapHelper 继承自RecyclerView.OnFlingListener
,并实现了它的抽象方法onFling
, 支持 SnapHelper 的RecyclerView.LayoutManager
必须实现RecyclerView.SmoothScroller.ScrollVectorProvider
接口,或者你自己实现onFling(int,int)
方法手动处理。SnapHeper 有以下几个重要方法:
-
attachToRecyclerView: 将 SnapHelper attach 到指定的 RecyclerView 上。
-
calculateDistanceToFinalSnap: 复写这个方法计算对齐到 TargetView 或容器指定点的距离,这是一个抽象方法,由子类自己实现,返回的是一个长度为 2 的 int 数组 out,out[0] 是 x 方向对齐要移动的距离,out[1] 是 y 方向对齐要移动的距离。
-
calculateScrollDistance: 根据每个方向给定的速度估算滑动的距离,用于 Fling 操作。
-
findSnapView:提供一个指定的目标 View 来对齐,抽象方法,需要子类实现
-
findTargetSnapPosition:提供一个用于对齐的 Adapter 目标 position,抽象方法,需要子类自己实现。
-
onFling:根据给定的 x 和 y 轴上的速度处理 Fling。
3
LinearSnapHelper & PagerSnapHelper
上面讲了 SnapHelper 的几个重要的方法和作用,SnapHelper 是一个抽象类,要使用SnapHelper,需要实现它的几个方法。而 Google 内置了两个默认实现类,LinearSnapHelper
和PagerSnapHelper
,LinearSnapHelper 可以使 RecyclerView 的当前 Item 居中显示(横向和竖向都支持),PagerSnapHelper 看名字可能就能猜到,使RecyclerView 像ViewPager 一样的效果,每次只能滑动一页(LinearSnapHelper 支持快速滑动), PagerSnapHelper 也是 Item 居中对齐。接下来看一下使用方法和效果。
(1) LinearSnapHelper
LinearSnapHelper
使当前 Item 居中显示,常用场景是横向的 RecyclerView, 类似ViewPager 效果,但是又可以快速滑动(滑动多页)。代码如下:
LinearLayoutManager manager = new LinearLayoutManager(getContext()); manager.setOrientation(LinearLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(manager);// 将SnapHelper attach 到RecyclrView LinearSnapHelper snapHelper = new LinearSnapHelper(); snapHelper.attachToRecyclerView(mRecyclerView);
代码很简单,new 一个 SnapHelper 对象,然后 Attach 到 RecyclerView 即可。
效果如下:
如上图所示,简单几行代码就可以用 RecyclerView 实现一个类似 ViewPager 的效果,并且效果更赞。可以快速滑动多页,当前页剧中显示,并且显示前一页和后一页的部分。如果使用 ViewPager 来做还是有点麻烦的。除了上面的效果外,如果你想要和 ViewPager 一样,限制一次只让它滑动一页,那么你就可以使用 PagerSnapHelper 了,接下来看一下PagerSnapHelper 的使用效果。
(2) PagerSnapHelper (在Android 25.1.0 support 包加入的)
PagerSnapHelper
的展示效果和LineSnapHelper
是一样的,只是 PagerSnapHelper 限制一次只能滑动一页,不能快速滑动。代码如下:
PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(mRecyclerView);
PagerSnapHelper效果如下:
上面展示的是 PagerSnapHelper 水平方向的效果,竖直方向的效果和 LineSnapHelper 竖直的方向的效果差不多,只是不能快速滑动,就不在介绍了,感兴趣的可以把它们的效果都试一下。
上面就是LineSnapHelper
和 PagerSnapHelper
的使用和效果展示,了解了它的使用方法和效果,接下来我们看一下它的实现原理。
4
SnapHelper原码分析
上面介绍了 SnapHelper 的使用,那么接下来我们来看一下 SnapHelper 到底是怎么实现的,走读一下源码:
(1) 入口方法,attachToRecyclerView
通过attachToRecyclerView
方法将 SnapHelper attach 到 RecyclerView,看一下这个方法做了哪些事情:
/**
*
* 1,首先判断attach的RecyclerView 和原来的是否是一样的,一样则返回,不一样则替换
*
* 2,如果不是同一个RecyclerView,将原来设置的回调全部remove或者设置为null
*
* 3,Attach的RecyclerView不为null,先2设置回调 滑动的回调和Fling操作的回调,
* 初始化一个Scroller 用于后面做滑动处理,然后调用snapToTargetExistingView
*
* */ public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException { if (mRecyclerView == recyclerView) { return; // nothing to do } if (mRecyclerView != null) { destroyCallbacks(); } mRecyclerView = recyclerView;