关于Android NestedScrolling及其各个方法的理解

一、概述

NestedScrollingParent 和 NestedScrollingChild 两个类是Android在support.v4中提供的,这是一套全新的嵌套滑动机制,用来实现一些传统的自定义ViewGroup事件分发处理所不能完成的效果。

按照传统事件分发角度来看,事件是由父View发起的,如果父View需要自己做滑动效果就要拦截掉事件并通过自己的onTouchEvent方法进行消耗,父View拦截之后,子View是没有机会接收到此事件,事件分发,只要拦截了,当前手势接下来的时间都会交给父View(拦截者)来处理;如果父View不需要,也就是不拦截事件,交给子View消耗,那么如果不使用特殊方法的话父View也没法再处理此事件。

而NestedScrolling机制不一样,它是父View和子View在滑动过程中的交互。

比如:子View在处理事件前,会将事件的信息传递给父View,父View可以决定是否消耗掉事件或者消耗一部分,然后子View将父View消耗的部分减去,自己在处理剩下的,如果处理完后还有剩下的,可以再次交给父View,由父View处理。RecyclerView就是实现了NestedScrollingChild2,在onTouchEvent方法中,消耗滑动事件前,通过嵌套机制,把滑动信息交给父View,父View消耗后,自己在滑动剩下的偏移量。

二、方法

在Lollipop及以上版本的View源码中多了这么几个方法:

void setNestedScrollingEnabled(boolean enabled);
boolean isNestedScrollingEnabled();
boolean startNestedScroll(@ScrollAxis int axes);
void stopNestedScroll();
boolean hasNestedScrollingParent();
boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
            int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow);
boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
            @Nullable int[] offsetInWindow);
boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
boolean dispatchNestedPreFling(float velocityX, float velocityY);

比如在RecyclerView源码中实现了NestScrollingChild 接口,并在onTouchEvent方法中就调用了dispatchNestedPreScroll :

public boolean onTouchEvent(MotionEvent e) {
        ...
        switch (action) {
            ...
            case MotionEvent.ACTION_MOVE: {
                ...
                if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) {
                    dx -= mScrollConsumed[0];
                    dy -= mScrollConsumed[1];
                    vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
                    // Updated the nested offsets
                    mNestedOffsets[0] += mScrollOffset[0];
                    mNestedOffsets[1] += mScrollOffset[1];
                }   
                ...
        }
        return true;
    }

在处理move事件前,将用户触发的dx ,dy,等信息传递给父View,父View可以将dx或者dy消耗或者部分消耗后,把剩余dx或dy交给子View,再由子View处理。

如果想要一个View能够产生NestScroll事件 首先这个View必须实现NestScrollChild接口(Android 5.0以上的所有View都实现了NestScrollChild接口),其次必须调用setNestedScrollingEnabled方法来将一个普通事件转换为NestScroll事件。

而在ViewGroup中多了这些方法:

public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
public void onStopNestedScroll(View target);
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed);
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();

下面是对这些方法的理解:

    /**
     * 只有在onStartNestedScroll返回true的时候才会接着调用onNestedScrollAccepted,
     * 这个判断是需要我们自己来处理的,
     * 不是直接的父子关系一样可以正常进行
     */
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) 

    /**
     * 字面意思可以理解出来父View接受了子View的邀请,可以在此方法中做一些初始化的操作。
     */
    public void onNestedScrollAccepted(View child, View target, int axes)

    /**
     * 每次子View在滑动前都需要将滑动细节传递给父View,
     * 一般情况下是在ACTION_MOVE中调用
     * public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow),
     * dispatchNestedPreScroll在ScrollView的Action_Move中被调用
     * 然后父View就会被回调public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)。
     */
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)

    /**
     * 接下来子View就要进自己的滑动操作了,滑动完成后子View还需要调用
     * public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow)
     * 将自己的滑动结果再次传递给父View,父View对应的会被回调
     * public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed),
     * 但这步操作有一个前提,就是父View没有将滑动值全部消耗掉,因为父View全部消耗掉,子View就不应该再进行滑动了
     * 子View进行自己的滑动操作时也是可以不全部消耗掉这些滑动值的,剩余的可以再次传递给父View,
     * 使父View在子View滑动结束后还可以根据子View剩余的值再次执行某些操作。
     */
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) 

    /**
     * ACTION_UP或者ACTION_CANCEL的到来,
     * 子View需要调用public void stopNestedScroll()来告知父View本次NestedScrollig结束,
     * 父View对应的会被回调public void onStopNestedScroll(View target),
     */
    public void onStopNestedScroll(View child) 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值