专栏首页刘晓杰嵌套滑动机制详解

嵌套滑动机制详解

嵌套滑动机制主要涉及两个接口

public interface NestedScrollingChild {
    void setNestedScrollingEnabled(boolean var1);
    boolean isNestedScrollingEnabled();

    boolean startNestedScroll(int var1);//开启嵌套滚动流程
    //循环遍历parent,先判断是否是NestedScrollingParent的实例,如果是,那就调用parent的onStartNestedScroll和onNestedScrollAccepted

    void stopNestedScroll();
    boolean hasNestedScrollingParent();
    boolean dispatchNestedScroll(int var1, int var2, int var3, int var4, @Nullable int[] var5);

    boolean dispatchNestedPreScroll(int var1, int var2, @Nullable int[] var3, @Nullable int[] var4);
    //在子view自己进行滚动之前调用此方法,询问父view是否要在子view之前进行滚动。
  //此方法的前两个参数用于告诉父View此次要滚动的距离;而第三第四个参数用于子view获取父view消费掉的距离和父view位置的偏移量。
  //第一第二个参数为输入参数,即常规的函数参数,调用函数的时候我们需要为其传递确切的值。而第三第四个参数为输出参数,调用函数时我们只需要传递容器(在这里就是两个数组),在调用结束后,我们就可以从容器中获取函数输出的值。
  //如果parent消费了一部分或全部距离,则此方法返回true。

    boolean dispatchNestedFling(float var1, float var2, boolean var3);
    boolean dispatchNestedPreFling(float var1, float var2);
}

public interface NestedScrollingParent {
    boolean onStartNestedScroll(@NonNull View var1, @NonNull View var2, int var3);//决定是否要配合其进行嵌套滚动

    void onNestedScrollAccepted(@NonNull View var1, @NonNull View var2, int var3);
    void onStopNestedScroll(@NonNull View var1);
    void onNestedScroll(@NonNull View var1, int var2, int var3, int var4, int var5);

    void onNestedPreScroll(@NonNull View var1, int var2, int var3, @NonNull int[] var4);
    //child调用dispatchNestedPreScroll(),这时可以回调到parent的OnNestedPreScroll(),parent可以在这个回调中先于child滚动。

    boolean onNestedFling(@NonNull View var1, float var2, float var3, boolean var4);
    boolean onNestedPreFling(@NonNull View var1, float var2, float var3);
    int getNestedScrollAxes();
}

对它们打log如下

E/child: startNestedScroll-----   是否开启
E/parent: onStartNestedScroll——是否配合

E/child: dispatchNestedPreScroll——如果为true,将事件给parent
E/parent: onNestedPreScroll——此时dy有数据而consume没数据,给consume数据赋值。赋完值马上回传
E/数据parent: dy=14,consumed[1]0
E/数据child: dy=14,consumed[1]14

所以一般嵌套滑动的child都有如下重要的函数

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        //按下
        case MotionEvent.ACTION_DOWN:
            lastY = (int) event.getRawY();
            startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL | ViewCompat.SCROLL_AXIS_VERTICAL);//开启嵌套滑动
            break;
        //移动
        case MotionEvent.ACTION_MOVE:
            ......
            if (dispatchNestedPreScroll(0, dy, consumed, offset)) {//如果找到了支持嵌套滑动的父类,父类进行了一系列的滑动
                Log.e("数据child", "dy=" + dy + ",consumed[1]" + consumed[1]);
                //获取滑动距离进行滑动,具体情况具体分析
                int remain = dy - consumed[1];
                if (remain != 0) {
                    scrollBy(0, -remain);
                }
            } else {//如果没有嵌套滑动,那么自己滑动
                scrollBy(0, -dy);
            }
            break;
        case MotionEvent.ACTION_UP:
            stopNestedScroll();
            break;
    }
    return true;
}

父类实例中最重要的函数

//在此可以判断参数target是哪一个子view以及滚动的方向,然后决定是否要配合其进行嵌套滚动
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
    Log.e("parent", "onStartNestedScroll");
    if (target instanceof MyNestedScrollingChild2) {
        return true;
    }
    return false;
}

//先于child滚动
//前3个为输入参数,最后一个是输出参数
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
    Log.e("parent", "onNestedPreScroll");
    Log.e("数据parent", "dy=" + dy + ",consumed[1]" + consumed[1]);
    if (showImg(dy) || hideImg(dy)) {//如果需要显示或隐藏图片,即需要自己(parent)滚动
        scrollBy(0, -dy);//滚动
        consumed[1] = dy;//告诉child我消费了多少
    }
}

参考文章:https://www.cnblogs.com/wjtaigwh/p/6398562.html


但是


一般情况下不大可能就是一个光秃秃的NestedScrollingChild。如果child是recycleview的情况呢(里面还有滑动事件,而且本身还继承NestedScrollingChild) 我们先来看看recycleview的onTouchEvent函数

switch (action) {
    case MotionEvent.ACTION_DOWN: {
        mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
        mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
        mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);

        int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
        if (canScrollHorizontally) {
            nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
        }
        if (canScrollVertically) {
            nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
        }
        startNestedScroll(nestedScrollAxis);//******************************************************************
    } break;

    case MotionEventCompat.ACTION_POINTER_DOWN: {
        mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
        mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
        mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
    } break;

    case MotionEvent.ACTION_MOVE: {
        final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
        if (index < 0) {
            Log.e(TAG, "Error processing scroll; pointer index for id " +
                    mScrollPointerId + " not found. Did any MotionEvents get skipped?");
            return false;
        }

        final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
        final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
        int dx = mLastTouchX - x;
        int dy = mLastTouchY - y;

        if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {//*************************************************
            dx -= mScrollConsumed[0];
            dy -= mScrollConsumed[1];
            vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
            // Updated the nested offsets
            mNestedOffsets[0] += mScrollOffset[0];
            mNestedOffsets[1] += mScrollOffset[1];
        }

        if (mScrollState != SCROLL_STATE_DRAGGING) {
            boolean startScroll = false;
            if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
                if (dx > 0) {
                    dx -= mTouchSlop;
                } else {
                    dx += mTouchSlop;
                }
                startScroll = true;
            }
            if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
                if (dy > 0) {
                    dy -= mTouchSlop;
                } else {
                    dy += mTouchSlop;
                }
                startScroll = true;
            }
            if (startScroll) {
                setScrollState(SCROLL_STATE_DRAGGING);
            }
        }

        if (mScrollState == SCROLL_STATE_DRAGGING) {//*****************************在这里随着手指的移动而移动,调用scrollByInternal
            mLastTouchX = x - mScrollOffset[0];
            mLastTouchY = y - mScrollOffset[1];

            if (scrollByInternal(
                    canScrollHorizontally ? dx : 0,
                    canScrollVertically ? dy : 0,
                    vtev)) {
                getParent().requestDisallowInterceptTouchEvent(true);
            }
        }
    } break;

具体parent代码也可以参考下面的文章(之所以加dispatchTouchEvent是为了增加快速滑动效果,否则滑动效果太生硬)

参考文章:http://blog.csdn.net/lmj121212/article/details/53046582

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • View的工作原理

    View的绘制流程是从ViewRoot的PerformTraversals方法开始的。它经过measure,layout,draw三个过程将view绘制出来。m...

    提莫队长
  • View的事件体系

    2.MotionEvent 手指触摸屏幕后的一系列事件,包括ACTION_DOWN,ACTION_MOVE,ACTION_UP

    提莫队长
  • 16(套接字)

    套接字描述符在Unix系统中是用文件描述符实现的。事实上,许多处理文件描述符函数(read和write)都可以处理文件描述符 要创建一个套接字,可以调用so...

    提莫队长
  • 找树根和孩子

    •【例3-1】找树根和孩子 【问题描述】   给定一棵树,输出树的根root,孩子最多的结点max以及他的孩子 【输入格式】   第一行:n(结点数<=100)...

    attack
  • LeetCode 周赛题解 211

    遍历字符串,记录每个字符第一次出现的位置。当某个字符再次出现时,说明找到了相同的两个字符,那就更新一下最大长度。

    ACM算法日常
  • BZOJ1485: [HNOI2009]有趣的数列(Catalan数,质因数分解求组合数)

    考虑到每个数的最小的质因数$ \geqslant 2$,因此极限复杂度为$O(n log n)$

    attack
  • 第1天:网易2018年校园招聘NLP算法工程师笔试试卷分析

      由于剑指offer题目全部刷完了,由于现在就开始秋招了,今天早上借此机会做了一份2018年网易秋招的NLP工程师笔试题,大家有兴趣的话可以做一下,检验自己最...

    stefan666
  • 拉格朗日插值

    存在性和唯一性的证明以后再补。。。。 拉格朗日插值 拉格朗日插值,emmmm,名字挺高端的:joy: 它有什么应用呢? 我们在FFT中讲到过 设n-1次多项式为...

    attack
  • codeforces 1077D(二分)

    dejavu1zz
  • 1026 逃跑的拉尔夫

    1026 逃跑的拉尔夫 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 年轻的...

    attack

扫码关注云+社区

领取腾讯云代金券