美文网首页Android开发Android技术知识Android开发经验谈
弹性滚动和惯性滚动效果的实现原理——CoordinatorLay

弹性滚动和惯性滚动效果的实现原理——CoordinatorLay

作者: 远方的风景2018 | 来源:发表于2019-04-02 11:43 被阅读23次

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

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

    CoordinatorLayout

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

    使用CoordinatorLayout需要在 build.gradle 加入:

    implementation 'com.android.support:design:26.1.0'
    

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

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

    vertical over-scroll

    [图片上传失败...(image-be99c9-1554176574416)]

    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

    上图描述了向下滑动过程中需要处理的关键逻辑,同理,向上滑动也采取类似的处理.

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

    
    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

    相关文章

      网友评论

        本文标题:弹性滚动和惯性滚动效果的实现原理——CoordinatorLay

        本文链接:https://www.haomeiwen.com/subject/akiwbqtx.html