Android中的NestedScrollingParent和NestedScrollingChild分析

来源:互联网 发布:淘宝代销能赚钱吗 编辑:程序博客网 时间:2024/05/22 03:19

在分析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的分析就到这里了.

0 0