前面介绍了OverScroll的使用,没看过文章的同窗能够先了解下《相似微信首页弹性滚动和惯性滚动效果的实现——OverScroll》java
接下来介绍OverScroll的实现原理。android
CoordinatorLayout
是在Support 包中功能强大的布局容器,它本质是一个 FrameLayout,然而它容许开发者经过自定义Behavior
协调各个子view,实现各类复杂酷炫的UI交互效果。git
使用CoordinatorLayout
须要在 build.gradle 加入:github
implementation 'com.android.support:design:26.1.0'
复制代码
网上不少关于CoordinatorLayout
的入门文章,这里笔者再也不赘述,所谓实践大于理论,本文讲述如何利用CoordinatorLayout+Behavior实现弹性滑动和惯性滑动,从侧面去理解它的使用原理.微信
本文实现相似微信首页的弹性滑动和惯性滑动效果,支持水平和垂直方向上的滚动,以下图所示:app
CoordinatorLayout
主要是经过Behavior
来协调子view,这里涉及到的Behavior
的关键方法以下:工具
方法 | 描述 |
---|---|
boolean onStartNestedScroll(CoordinatorLayout parent, View child, View directTargetChild, View target, int nestedScrollAxes, int type) | 根据返回值判断是否要处理target 发生的滑动,通常用于判断是否要处理某个方向上的滑动.例如 return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 表示处理垂直方向方向上的滑动.接下来的滑动事件将回调给下面的方法处理. |
void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed, int type) | 当target 即将发生滑动时调用,在这里能够作拦截处理.能够修改参数consumed 表示消耗(拦截)了多少像素。例如target 控件自己想要垂直方向上滑动100px,而咱们须要拦截掉80px,则要设置 consumed[1] = 80 ,(consumed[0] 、consumed[1] 分别对应x轴和y轴),最后target 控件实际只滑动了20px. |
void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) | target 控件发生滑动后调用, dyConsumed为实际消耗的距离,dyUnconsumed为未消耗的距离.例如上面的滑动,此时dyConsumed = 20 ,dxUnconsumed = 0 ,若是dyConsumed = 15 ,dxUnconsumed = 5 则表示target 在滑动15px距离时到达了边界,咱们能够利用dxUnconsumed 处理一些越界后的滑动. |
boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) | 用户快速滑动target 并松开手指发生惯性滑动以前调用,返回true 表示拦截该惯性滑动事件. |
void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int type) | 全部的滑动中止后调用. |
弹性滑动和惯性滑动都属于过分滑动(Over scroll),即在达到正常滑动范围的边界后继续滑动.所以,咱们只须要处理在达到边界时的越界滑动效果.关键的处理逻辑以下:布局
上图描述了向下滑动过程当中须要处理的关键逻辑,同理,向上滑动也采起相似的处理.post
另外咱们还要处理惯性滑动,当快速滑动产生fling事件时,让列表滑到边界时仍可以惯性滑动一点距离.这里须要借助系统提供的工具类OverScroller
, 主要在onNestedPreFling
里相关的惯性滑动的参数传入OverScroller
.gradle
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
if (child == target) {
mOverScroller.fling(0, 0, 0, (int) velocityY, 0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
复制代码
后面即可以在滑动过程当中经过mOverScroller.getCurrVelocity()
获取当前时间惯性滑动的速度,当速度小于某个值时则中止滑动.
在Behavior
中的关键方法的参数中基本上最后都有个type值,文档解释为the type of input which cause this scroll event
,即表示产生当前滑动的事件来源,当type == ViewCompat.TYPE_TOUCH
时表示由用户触摸控件产生的滑动,type == ViewCompat.TYPE_NON_TOUCH
时表示由非触摸产生的滑动,好比惯性形成的滑动就是非触摸产生的.
所以咱们在滑动过程当中能够经过type判断当前滑动是否为惯性滑动.
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
if (type == ViewCompat.TYPE_TOUCH) { // scroll
} else { // fling
}
}
复制代码
最后,咱们须要在停滑动时,若是列表发生了越界偏移,则须要把列表弹回原位,这里经过ValueAnimator动画实现便可.
弹性滑动和惯性滑动过程当中须要一些参数控制滑动效果,如最大的滑动距离,惯性滑动的最小速度,滑动的阻尼因子等,所以咱们须要定义一个接口,和'Behavior`绑定的子View必须实现该接口,接口定义请查看IOverScrollCallback,默认实现为SimpleOverScrollCallback.
自定义Behavior弹性滑动和惯性滑动,基类为BaseOverScrollBehavior,控制垂直滚动OverScrollVerticalBehavior,控制水平滚动OverScrollHorizontalBehavior.
让NestedScrolling滑动控件(如RecyclerView
,NestedScrollView
等)实现IOverScrollCallback
,提供相关滑动参数,这里以OverScrollScrollView
控件为例,代码请查看OverScrollScrollView.
效果(布局相关:NestedScrollFragment ,layout_scrollview.xml):
多谢支持个人github项目>>>OverScroll!