美文网首页
NestedScrolling 0

NestedScrolling 0

作者: 比目鱼26 | 来源:发表于2017-04-18 15:20 被阅读0次

    Why

    Android事件分发机制一般情况下能满足大部分情况下View事件处理,其中的关键点就是事件拦截和事件上传,具体的详情这里不做展开。假设现在有这样一种场景,ViewGroup有且仅有一个Child View,�
    要求是Child消费down事件、ViewGroup消费move事件,这种情况就不太好处理,原因是Child消费down事件后,后续的move、up和cancel的onTouchEvent返回false, 事件都会发送到Child, ViewGroup不会有机会处理。可以看出down事件是否消费很关键,根据默认的事件分发机制似乎不能完成这里的要求,好在google提供了design(Android L版本提出), design库其中的一个重要特性就是嵌套滑动机制。�

    What

    嵌套滑动可以简单的理解成父(ViewGroup)和子(View或者ViewGroup)共同处理move事件,流程是这样的:�子View先将滑动事件交给父View处理,�父View根据情况消耗全部或者部分move偏移量,子View再消耗剩余的move偏移量,最后子View再通知父View scroll消耗的情况,父View根据这个需求再进行滑动,整个过程的顺序如下:

    0,父View先处理scroll
    1,子View再处理scroll
    2,父View再处理scroll
    3,子View再处理scroll

    元素

    主要接口有2个,分别是 NestedScrollingParent
    NestedScrollingChild
    ,父 View 需要实现 NestedScrollingParent接口,而子 View 需要实现 NestedScrollingChild接口。

    NestedScrollingChild接口的源码如下:

    public interface NestedScrollingChild {
        //设置是否开启嵌套滑动功能
        public void setNestedScrollingEnabled(boolean enabled);
        public boolean isNestedScrollingEnabled();
        //开始一个新的嵌套滑动,参数指定横向或者纵向
        public boolean startNestedScroll(int axes);
        //结束一个已经开始的嵌套
        public void stopNestedScroll();
        //是否有对应的parent配合处理滑动事件
        public boolean hasNestedScrollingParent();
        //scroll的预处理过程,对应上图的0和1
        public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
        //scroll的后处理过程,对应上图的2和3
        public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
                int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
        //fling和scroll和类似
        public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
        public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
    }
    

    NestedScrollingParent接口的源码如下:

    public interface NestedScrollingParent {
        //是否接受子View的嵌套滑动
        public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
        //成功后的回调
        public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
        //结束后的回调
        public void onStopNestedScroll(View target);
        //预处理过程,对应上图的0
        public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
        //后处理过程,对应上图的2
        public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
                int dxUnconsumed, int dyUnconsumed);
        //fling和scroll和类似
        public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
        public boolean onNestedPreFling(View target, float velocityX, float velocityY);
        //横向还是纵向
        public int getNestedScrollAxes();
    }
    

    google还提供了2个辅助类来帮助实现整个过程,分别是NestedScrollingChildHelperNestedScrollingParentHelper,从名字可以看出分别作用的对象是Child和Parent。

    比如Child的用法如下:

    // NestedScrollingChild
    
        @Override
        public void setNestedScrollingEnabled(boolean enabled) {
            getScrollingChildHelper().setNestedScrollingEnabled(enabled);
        }
    
        @Override
        public boolean isNestedScrollingEnabled() {
            return getScrollingChildHelper().isNestedScrollingEnabled();
        }
    
        @Override
        public boolean startNestedScroll(int axes) {
            return getScrollingChildHelper().startNestedScroll(axes);
        }
    
        @Override
        public void stopNestedScroll() {
            getScrollingChildHelper().stopNestedScroll();
        }
    
        @Override
        public boolean hasNestedScrollingParent() {
            return getScrollingChildHelper().hasNestedScrollingParent();
        }
    
        @Override
        public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
                int dyUnconsumed, int[] offsetInWindow) {
            return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
                    dxUnconsumed, dyUnconsumed, offsetInWindow);
        }
    
        @Override
        public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
            return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
        }
    
        @Override
        public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
            return getScrollingChildHelper().dispatchNestedFling(velocityX, velocityY, consumed);
        }
    
        @Override
        public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
            return getScrollingChildHelper().dispatchNestedPreFling(velocityX, velocityY);
        }
    

    都是直接的调用helper的方法,可以看出google为了减少开发者的学习和开发成本费了很多心思。Parent的helper相比于Child提供的方法就不是那么多了,主要的逻辑主要靠开发者自己实现。

    流程

    Child Parent
    startNestedScroll onStartNestedScroll、onNestedScrollAccepted
    dispatchNestedPreScroll onNestedPreScroll
    dispatchNestedScroll onNestedScroll
    stopNestedScroll onStopNestedScroll

    可以看出是一一对应的关系,整个过程是Child主导,Parent配合完成,用文字说明一下整个过程:

    1. 准备过程

    Child发起一个嵌套滑动处理,调用startNestedScroll.
    Parent回调onStartNestedScroll说明收到了来自Child的请求,根据情况决定是否接受Child请求,通过返回值告诉Child结果。
    Parent如果接受Child的请求还会回调onNestedScrollAccepted。

    2. 处理过程

    每次滑动前,Child 先询问 Parent 是否需要滑动,即 dispatchNestedPreScroll(),这就回调到 Parent 的 onNestedPreScroll(),Parent 可以在这个回调中“劫持”掉 Child 的滑动,也就是先于 Child 滑动。

    Child 滑动以后,会调用 onNestedScroll(),回调到 Parent 的 onNestedScroll(),这里就是 Child 滑动后,剩下的给 Parent 处理,也就是 后于 Child 滑动。

    3. 结束过程

    Child调用stopNestedScroll发起一个结束请求,Parent回调回调onStopNestedScroll处理相关逻辑,比如重置参数等待下次滑动请求

    版本兼容

    虽然是Api21版本才提出的,google也提供了兼容方案,相关接口再android.support.v4包中

    相关文章

      网友评论

          本文标题:NestedScrolling 0

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