OverScroll弹性滚动和惯性滚动效果的实现原理——CoordinatorLayout+Behavior

前面介绍了OverScroll的使用,没看过文章的同窗能够先了解下《相似微信首页弹性滚动和惯性滚动效果的实现——OverScroll》java

接下来介绍OverScroll的实现原理。android

CoordinatorLayout

CoordinatorLayout是在Support 包中功能强大的布局容器,它本质是一个 FrameLayout,然而它容许开发者经过自定义Behavior协调各个子view,实现各类复杂酷炫的UI交互效果。git

使用CoordinatorLayout须要在 build.gradle 加入:github

implementation 'com.android.support:design:26.1.0'
复制代码

网上不少关于CoordinatorLayout的入门文章,这里笔者再也不赘述,所谓实践大于理论,本文讲述如何利用CoordinatorLayout+Behavior实现弹性滑动和惯性滑动,从侧面去理解它的使用原理.微信

本文实现相似微信首页的弹性滑动和惯性滑动效果,支持水平和垂直方向上的滚动,以下图所示:app

vertical over-scroll
horizontal over-scroll

Behavior

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),即在达到正常滑动范围的边界后继续滑动.所以,咱们只须要处理在达到边界时的越界滑动效果.关键的处理逻辑以下:布局

overscoll

上图描述了向下滑动过程当中须要处理的关键逻辑,同理,向上滑动也采起相似的处理.post

另外咱们还要处理惯性滑动,当快速滑动产生fling事件时,让列表滑到边界时仍可以惯性滑动一点距离.这里须要借助系统提供的工具类OverScroller, 主要在onNestedPreFling里相关的惯性滑动的参数传入OverScrollergradle

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.

效果(布局相关:NestedScrollFragmentlayout_scrollview.xml):

nested over-scroll

项目地址OverScroll

多谢支持个人github项目>>>OverScroll

相关文章
相关标签/搜索