zoukankan      html  css  js  c++  java
  • Android中的NestedScrollingParent和NestedScrollingChild分析

    在分析SwipeRefreshLayout源码的时候发现该类实现了NestedScrollingParent和NestedScrollingChild两个接口,甚是好奇,于是结合了网上的资料,然后根据我个人的理解写下本章. 
    这个两个接口是为了更好解决事件冲突的. 
    在这里 nested scrolling 就翻译为嵌套滚动吧. 
    但是这和以前用过的dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent和requestDisallowInterceptTouchEvent有什么区别呢?其实我感觉NestedScrollingParent和NestedScrollingChild是辅助解决事件冲突出现的.在之前的事件拦截中,就算子View的ACTION_MOVE的事件返回false,父布局也是获取不了该事件的.

    NestedScrollingChild接口的定义如下:

    public interface NestedScrollingChild {
        // 参数enabled:true表示view使用嵌套滚动,false表示禁用.
        public void setNestedScrollingEnabled(boolean enabled);
    
        public boolean isNestedScrollingEnabled();
    
        // 参数axes:表示滚动的方向如:ViewCompat.SCROLL_AXIS_VERTICAL(垂直方向滚动)和
        // ViewCompat.SCROLL_AXIS_HORIZONTAL(水平方向滚动)
        // 返回值:true表示本次滚动支持嵌套滚动,false不支持
        public boolean startNestedScroll(int axes);
    
        public void stopNestedScroll();
    
        public boolean hasNestedScrollingParent();
    
        // 参数dxConsumed: 表示view消费了x方向的距离长度
        // 参数dyConsumed: 表示view消费了y方向的距离长度
        // 参数dxUnconsumed: 表示滚动产生的x滚动距离还剩下多少没有消费
        // 参数dyUnconsumed: 表示滚动产生的y滚动距离还剩下多少没有消费
        // 参数offsetInWindow: 表示剩下的距离dxUnconsumed和dyUnconsumed使得view在父布局中的位置偏移了多少
        public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
                int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
    
        // 参数dx: 表示view本次x方向的滚动的总距离长度
        // 参数dy: 表示view本次y方向的滚动的总距离长度
        // 参数consumed: 表示父布局消费的距离,consumed[0]表示x方向,consumed[1]表示y方向
        // 参数offsetInWindow: 表示剩下的距离dxUnconsumed和dyUnconsumed使得view在父布局中的位置偏移了多少
        public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
    
        // 这个是滑动的就不详细分析了
        public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
    
        public boolean dispatchNestedPreFling(float velocityX, float velocityY);
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    1,setNestedScrollingEnabled 实现该结构的View要调用setNestedScrollingEnabled(true)才可以使用嵌套滚动.

    2,isNestedScrollingEnabled判断当前view能否使用嵌套滚动.

    3,startNestedScroll和stopNestedScroll.是配对使用的.startNestedScroll表示view开始滚动了,一般是在ACTION_DOWN中调用,如果返回true则表示父布局支持嵌套滚动.在事件结束比如ACTION_UP或者ACTION_CANCLE中调用stopNestedScroll,告诉父布局滚动结束.

    4,dispatchNestedScroll,把view消费滚动距离之后,把剩下的滑动距离再次传给父布局.

    5,dispatchNestedPreScroll,在view消费滚动距离之前把总得滑动距离传给父布局.

    6,dispatchNestedFling和dispatchNestedPreFling就是view传递滑动的信息给父布局的.

    NestedScrollingParent接口的定义如下:

    public interface NestedScrollingParent {
    
          // 参数child:ViewParent包含触发嵌套滚动的view的对象
          // 参数target:触发嵌套滚动的view  (在这里如果不涉及多层嵌套的话,child和target)是相同的
          // 参数nestedScrollAxes:就是嵌套滚动的滚动方向了.
        public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
    
        public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
    
        public void onStopNestedScroll(View target);
    
        // 参数target:同上
        // 参数dxConsumed:表示target已经消费的x方向的距离
        // 参数dyConsumed:表示target已经消费的x方向的距离
        // 参数dxUnconsumed:表示x方向剩下的滑动距离
        // 参数dyUnconsumed:表示y方向剩下的滑动距离
        public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
                int dxUnconsumed, int dyUnconsumed);
    
        // 参数dx:表示target本次滚动产生的x方向的滚动总距离
        // 参数dy:表示target本次滚动产生的y方向的滚动总距离
        // 参数consumed:表示父布局要消费的滚动距离,consumed[0]和consumed[1]分别表示父布局在x和y方向上消费的距离.
        public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
    
        public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
    
        public boolean onNestedPreFling(View target, float velocityX, float velocityY);
    
        public int getNestedScrollAxes();
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    1,onStartNestedScroll.当子view的调用NestedScrollingChild的方法startNestedScroll时,会调用该方法.

    2,onNestedScrollAccepted.如果onStartNestedScroll方法返回的是true的话,那么紧接着就会调用该方法.它是让嵌套滚动在开始滚动之前,让布局容器(viewGroup)或者它的父类执行一些配置的初始化的.下面是原文: 
    (It offers an opportunity for the view and its superclasses to perform initial configuration for the nested scroll.)

    3,onStopNestedScroll停止滚动了,当子view调用stopNestedScroll时会调用该方法.

    4,onNestedScroll,当子view调用dispatchNestedScroll方法时,会调用该方法.

    5,onNestedPreScroll,当子view调用dispatchNestedPreScroll方法是,会调用该方法.

    6,dispatchNestedFling和dispatchNestedPreFling对应的就是滑动了.

    下面给出基本的例子,首先是父布局的代码:

    package com.yluo.testnestscrolling;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.support.v4.view.NestedScrollingParent;
    import android.support.v4.view.NestedScrollingParentHelper;
    import android.support.v4.view.ViewCompat;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    import android.widget.FrameLayout;
    
    @SuppressLint("NewApi") public class NestScrollingLayout extends FrameLayout implements NestedScrollingParent{
    
        private static final String TAG = "NestScrollingLayout";
    
        private  NestedScrollingParentHelper mParentHelper;
    
        public NestScrollingLayout(Context context, AttributeSet attrs,
                int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        public NestScrollingLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public NestScrollingLayout(Context context) {
            super(context);
            init();
    
        }
    
        @SuppressLint("NewApi") private void init() {
             mParentHelper = new NestedScrollingParentHelper(this);
        }
        @Override
        public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
    
            Log.d(TAG, "child==target:" + (child == target));
    
            Log.d(TAG, "----父布局onStartNestedScroll----------------");
    
             return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
        }
        @SuppressLint("NewApi") @Override
        public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
    
            Log.d(TAG, "----父布局onNestedScrollAccepted---------------");
    
            mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
        }
    
        @Override
        public void onStopNestedScroll(View target) {       
            Log.d(TAG, "----父布局onStopNestedScroll----------------");
            mParentHelper.onStopNestedScroll(target);
        }
    
        @Override
        public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
                int dxUnconsumed, int dyUnconsumed) {
            Log.d(TAG, "----父布局onNestedScroll----------------");
        }
    
        @Override
        public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
            scrollBy(0, -dy);
    
            consumed[0] = 0;
            consumed[1] = 10; // 把消费的距离放进去
            Log.d(TAG, "----父布局onNestedPreScroll----------------");
        }
    
        @Override
        public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
            Log.d(TAG, "----父布局onNestedFling----------------");
            return true;
        }
        @Override
        public boolean onNestedPreFling(View target, float velocityX, float velocityY)  {
            Log.d(TAG, "----父布局onNestedPreFling----------------");
            return true;
        }
        @Override
        public int getNestedScrollAxes() {
            Log.d(TAG, "----父布局getNestedScrollAxes----------------");
             return mParentHelper.getNestedScrollAxes();
        }   
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    接着是子View的代码:

    package com.yluo.testnestscrolling;
    
    import android.content.Context;
    import android.support.v4.view.NestedScrollingChild;
    import android.support.v4.view.NestedScrollingChildHelper;
    import android.support.v4.view.ViewCompat;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    public class NestScrollingView extends View implements NestedScrollingChild{
    
        private static final String TAG = "NestScrollingView";
    
        private NestedScrollingChildHelper mChildHelper;
    
        private int[] mConsumed = new int[2];
    
        private int[] mOffset = new int[2];
    
        public NestScrollingView(Context context, AttributeSet attrs,
                int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        public NestScrollingView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public NestScrollingView(Context context) {
            super(context);
            init();
        }
    
        private void init() {
            mChildHelper = new NestedScrollingChildHelper(this);
            setNestedScrollingEnabled(true);
        }
    
        @Override
        public void setNestedScrollingEnabled(boolean enabled) {
            mChildHelper.setNestedScrollingEnabled(enabled);
        }
        @Override
        public boolean isNestedScrollingEnabled() {
            return mChildHelper.isNestedScrollingEnabled();
        }
        @Override
        public boolean startNestedScroll(int axes) {
            Log.d(TAG, "-----------子View开始滚动---------------");
            return mChildHelper.startNestedScroll(axes);
        }
        @Override
        public void stopNestedScroll() {
            Log.d(TAG, "-----------子View停止滚动---------------");
            mChildHelper.stopNestedScroll();
        }
        @Override
        public boolean hasNestedScrollingParent() {
            return mChildHelper.hasNestedScrollingParent();
        }
        @Override
         public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
                    int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
            Log.d(TAG, "-----------子View把剩余的滚动距离传给父布局---------------");
    
            return mChildHelper.dispatchNestedScroll(dxConsumed,dyConsumed,
                    dxUnconsumed,dyUnconsumed,offsetInWindow);
        }
        @Override
         public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
            Log.d(TAG, "-----------子View把总的滚动距离传给父布局---------------");
            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); 
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) { 
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 按下事件调用startNestedScroll
                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
                break;
            case MotionEvent.ACTION_MOVE:
                // 移动事件调用startNestedScroll
                dispatchNestedPreScroll(0,20,mConsumed,mOffset);
                // 输出一下偏移
                Log.d(TAG, "offset--x:" + mOffset[0] + ",offset--y:" + mOffset[1]);
                dispatchNestedScroll(50,50,50,50,mOffset);
    
                break;
            case MotionEvent.ACTION_UP:
            // 弹起事件调用startNestedScroll
                stopNestedScroll();
                break;
            default:
                break;
            }
            return true;
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113

    布局文件如下:

    <com.yluo.testnestscrolling.NestScrollingLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
     >
    
        <com.yluo.testnestscrolling.NestScrollingView
            android:layout_marginTop="100dp"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="@android:color/holo_green_dark">
    
        </com.yluo.testnestscrolling.NestScrollingView>
    
    </com.yluo.testnestscrolling.NestScrollingLayout>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出结果如下:

    --------------子View开始滚动------------------
    ----父布局onStartNestedScroll----------------
     ----父布局onNestedScrollAccepted---------------
    -----------子View把总的滚动距离传给父布局--------
    ----父布局onNestedPreScroll----------------
    ---offset--x:0,offset--y:20
    -----------子View把剩余的滚动距离传给父布局-------
     ----父布局onNestedScroll----------------
     -----------子View停止滚动---------------
     ----父布局onStopNestedScroll----------------
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输入的顺序跟之前我们分析的是一样的.好了NestedScrollingParent和NestedScrollingChild的分析就到这里了.

  • 相关阅读:
    LINQ -2015-04-27
    wireshark的安装
    c#中的classes和objects一些知识【1】
    初学C#,用vs去开始hello world!
    file_get_contents HTTP request failed! Internal Server Error
    验证码二(验证码使用)
    接口调用 POST
    接口调用 GET方式
    百度地图改标注样式
    Linux-常用命令
  • 原文地址:https://www.cnblogs.com/vegetate/p/9997230.html
Copyright © 2011-2022 走看看