您好,欢迎来到源码搜藏!分享精神,快乐你我!提示:担心找不到本站?在百度搜索“源码搜藏”,网址永远不丢失!
  • 首 页
  • 在线工具
  • 当前位置:首页 > 安卓源码 > 技术博客 >

    NestedScrolling(Android嵌套滑动机制)

    时间:2016-12-29 16:27 来源:互联网 作者:源码搜藏 浏览:收藏 挑错 推荐 打印

    具体效果可以对比一下: 重点文字标记在segmentfault上支持 code标签 ,简书上最多只能通过粗体实现。(反正我是没有找到更好的方法) 说到Gemini,我也是这两天因为了解 NestedScrolling 时接触到的,粗略看了一下资料和文章浏览数,赞! 我的大神! 好,前番

    具体效果可以对比一下:
    NestedScrolling(Android嵌套滑动机制)NestedScrolling(Android嵌套滑动机制)

    重点文字标记在segmentfault上支持code标签,简书上最多只能通过粗体实现。(反正我是没有找到更好的方法)

    说到Gemini,我也是这两天因为了解NestedScrolling时接触到的,粗略看了一下资料和文章浏览数,赞! 我的大神!

    好,前番就到这了,开始正题NestedScrolling

    之前了解NestedScrolling的时候看过一些博客,其中就包括Gemini的segmentfault,当时看的时候因为不仔细不以为然,最后才发现这篇博客是对NestedScrolling介绍最清楚的,作为惩罚也好膜拜也罢,把本来可以cv过来的博客手动敲一遍,顺便补充一下自己的一些额外理解。

    再次感谢Gemini


    Android 在发布 Lillipop 版本后,为了更好的用户体验,Google为Android的滑动机制提供了NestedScrolling机制。

    NestedScrolling的特性可以体现在哪儿呢?
    比如你用了Toolbar,下面一个ScrollView,向上滚动隐藏Toolbar,向下滚动显示Toolbar,这里在逻辑上就是一个NestedScrolling ——因为你在滚动整个Toolbar在内的View的过程中,又嵌套滚动了里边的ScrollView

    如图:
    NestedScrolling(Android嵌套滑动机制)

    在这之前,我们知道Android对Touch事件分发是有自己的一套机制。主要是有三个函数:
    dispatchTouchEventonInterceptTouchEventonTouchEvent

    这种分发机制有一个漏洞:

    如果子view获得处理touch事件机会的时候,父view就再也没有机会处理此次touch事件,直到下一次手指触发。

    也就是说,我们在滑动子view的时候,如果子view对这个滑动事件不需要处理的时候,只能抛弃这个touch事件,而不会传给父view去处理。

    但Google新的NestedScrolling机制就很好的解决了这个问题。

    NestedScrolling主要有四个类需要关注:

    NestedScrollingChild
    NestedScrollingChildHelper
    NestedScrollingParent
    NestedScrollingParentHelper

    以上四个类都在support-v4包中提供,Lollipop中部分View默认实现了NestedScrollingChildNestedScrollingParent

    v4包中NestedScrollView同时实现了NestedScrollingChild和NestedScrollingParent。

    一般实现NestedScrollingChild就可以了,父View用support-design提供的实现了NestedScrollingParentCoordinatorLayout即可。

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

    简单逻辑这样就可以实现嵌套滑动。

    以上接口都是业务逻辑中自己调用,NestedScrollingChildHelper是如何实现的呢? 先看一下startNestedScroll方法

    /**
         * Start a new nested scroll for this view.
         *
         * <p>This is a delegate method. Call it from your {@link android.view.View View} subclass
         * method/{@link NestedScrollingChild} interface method with the same signature to implement
         * the standard policy.</p>
         *
         * @param axes Supported nested scroll axes.
         *             See {@link NestedScrollingChild#startNestedScroll(int)}.
         * @return true if a cooperating parent view was found and nested scrolling started successfully
         */
        public boolean startNestedScroll(int axes) {
            if (hasNestedScrollingParent()) {
                // Already in progress
                return true;
            }
            if (isNestedScrollingEnabled()) {
                ViewParent p = mView.getParent();
                View child = mView;
                while (p != null) {
                    if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
                        mNestedScrollingParent = p;
                        ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
                        return true;
                    }
                    if (p instanceof View) {
                        child = (View) p;
                    }
                    p = p.getParent();
                }
            }
            return false;
        }

    可以看到这里是帮你实现了与NestedScrollingParent交互的一些方法。
    ViewParentCompat是一个和父View交互的兼容类,判断API version,如果在Lollipop上就调用View自带的方法,否则判断如果实现了NestedScrollingParent,则调用实现接口的方法。

    子View与父View的交互流程如下:

    一、startNestedScroll

    首先子View需要开启整个流程(通过屏幕滑动触发touch事件),通过NestedScrollingChildHelper找到并通知实现了NestedScrollingParent的父View中onStartNestedScrollonNestedScrollAccepted方法。

    二、dispatchNestedPreScroll

    在子View的onIterceptTouchEventonTouch中(一般在MontionEvent.ACTION_MOVE事件里),调用改方法通知父View的滑动距离,该方法的第三第四个参数返回父View消费掉的scroll长度和子View的窗口偏移量,如果这个scroll没有被消费完,则子View处理剩余距离,由于窗口被移动,如果记录了手指最后的位置,需要根据第四个参数offsetInWindow计算偏移量,才能保证下一次touch事件的计算是正确的。

    如果父View接受了滚动参数并部分消费,则该函数返回true,否则返回false。该函数一般在子View处理Scroll前调用。

    三、dispatchNestedScroll

    向父View汇报滚动情况,包括子View已消费和未消费的值。
    如果父View接受了滚动参数,部分消费则函数返回true,否则返回false。
    该函数一般在子View处理Scroll后调用。

    四、stopNestedScroll

    结束整个嵌套滑动流程。

    流程中NestedScrollingChildNestedScrollingParent对应如下:

    NestedScrollingChildImpl NestedScrollingParentImpl
    onStartNestedScroll onStartNestedScrollonNestedScrollAccepted
    dispatchNestedPreScroll onNestedPreScroll
    dispatchNestedScroll onNestedScroll
    stopNestedScroll onStopNestedScroll

    一般是子View发起调用,父View接受回调。

    需要关注dispatchNestedPreScroll中的consumed参数:

    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) ;
    

    该参数是一个int类型的数组,长度为2,第一个元素是父View消费的x轴方向的滚动距离,第二个元素是父View消费的y轴方向的滚动距离,如果两个值均不为0,则表示父View已消费滚动距离,则需要对子View滚动距离进行修正,正因为有该参数,使得处理滚动事件时思路更加清晰,不会像以前一样被一堆滚动参数搞混。


    自己理解的NestedScrolling简要流程图(不包含Fling事件及返回值的逻辑):

    NestedScrolling(Android嵌套滑动机制)

    NestedScrolling(Android嵌套滑动机制)转载http://www.codesocang.com/anzhuoyuanma/boke/34251.html
    标签:网站源码