前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >嵌套滑动机制详解

嵌套滑动机制详解

作者头像
提莫队长
发布2018-05-18 15:11:37
7610
发布2018-05-18 15:11:37
举报
文章被收录于专栏:刘晓杰

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

代码语言:javascript
复制
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如下

代码语言:javascript
复制
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都有如下重要的函数

代码语言:javascript
复制
@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;
}

父类实例中最重要的函数

代码语言:javascript
复制
//在此可以判断参数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函数

代码语言:javascript
复制
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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年01月24日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档