Android NestedScrolling嵌套滑动实现

安卓实现嵌套滚动的机制解析

要知道在安卓中,处理事件分发的机制和滑动冲突都是通过View的dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent这三个方法互相配合完成的。
但是这种机制是由父View发起,一直向下传递,而且这个机制有个缺点:

父View将事件分发下去之后,一旦有子View决定处理该系列事件(即子View的onTouchEvent返回了true),那么该系列事件将会一直下发给子View处理,即使子View在某个onTouch事件中不想处理(即返回false),那它的父View也不会再继续处理此系列事件,除非父View拦截某个事件,但是父View拦截了touch事件之后,该系列事件就不会下发给子View处理了

于是乎就有了NestedScrolling机制了,NestedScrolling机制中,通过子View在处理滑动事件的手告诉父View我要开始滑动了,此时父View便会决定要不要跟着一起动,或者会寻找有没有另外的子View要跟着一起滑动。NestedScrolling机制主要依赖以下四个类:

  1. NestedScrollingChild
  2. NestedScrollingParent
  3. NestedScrollingChildHelper
  4. NestedScrollingParentHelper

一般作为滑动列表的子View实现NestedScrollingChild接口,作为parent的父View则实现NestedScrollingParent,这样滑动的子View就可以将滑动事件分发给父View,并且父View也可以将滑动事件分发给其他的子View了。

贴出NestedScrollingChild和NestedScrollingParent两个接口的API解释:

public interface NestedScrollingChild {
   

    /**
     * 设置嵌套滑动是否能用
     */
    @Override
    public void setNestedScrollingEnabled(boolean enabled);

    /**
     * 判断嵌套滑动是否可用
     */
    @Override
    public boolean isNestedScrollingEnabled();

    /**
     * 开始发起嵌套滑动
     *
     * @param axes 表示方向轴,有横向和竖向
     */
    @Override
    public boolean startNestedScroll(int axes);

    /**
     * 停止嵌套滑动
     */
    @Override
    public void stopNestedScroll();

    /**
     * 判断是否有支持嵌套滑动的父View
     */
    @Override
    public boolean hasNestedScrollingParent() ;

    /**
     * 惯性滑动时调用
     * @param velocityX x 轴上的滑动速率
     * @param velocityY y 轴上的滑动速率
     * @param consumed 是否被消费
     * @return  true 如果惯性滑动被父View或其它子View消耗
     */
    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) ;

    /**
     * 进行惯性滑动前调用
     * @param velocityX x 轴上的滑动速率
     * @param velocityY y 轴上的滑动速率
     * @return true 父View消耗了惯性滑动
     */
    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) ;

    /**
     * 子view处理滑动之后调用(即子view滑动完之后通知父view)
     * @param dxConsumed x轴上被消费的距离(横向)
     * @param dyConsumed y轴上被消费的距离(竖向)
     * @param dxUnconsumed x轴上未被消费的距离
     * @param dyUnconsumed y轴上未被消费的距离
     * @param offsetInWindow 子View的窗体偏移量
     * @return  true 事件被父View处理, false 事件没有被父View处理
     */
    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow) ;

    /**
     * 在子View的onInterceptTouchEvent或者onTouch中,调用该方法通知父View滑动的距离(即在子view处理滑动之前通知父View是否要消耗滑动事件)
     * @param dx  x轴上滑动的距离
     * @param dy  y轴上滑动的距离
     * @param consumed 父view消费掉的滑动长度
     * @param offsetInWindow   子View的窗体偏移量
     * @return 支持的嵌套的父View 是否处理了 滑动事件
     */
    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed, @Nullable int[] offsetInWindow);

}
public interface NestedScrollingParent {
   
        /**
         * 回应子view发起的嵌套滑动
         * @param child 实现了NestedScrollingParent的view
         * @param target 发起嵌套滑动的子view
         * @param axes 滑动方向
         * @return  true 表示接受嵌套滑动
         */
        boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes);

        /**
         * 接受嵌套滑动时回调
         * @param child 实现了NestedScrollingParent的view
         * @param target 发起嵌套滑动的子view
         * @param axes 滑动方向
         */
        void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes);

        /**
         * 嵌套滑动结束回调
         * @param target 发起嵌套滑动的子view
         */
        void onStopNestedScroll(@NonNull View target);

        /**
         * 回应正在进行的嵌套滑动(后于子view)
         * @param target 发起嵌套滚动的子view
         * @param dxConsumed 已经消耗的水平滚动距离
         * @param dyConsumed 已经消耗的垂直滚动距离
         * @param dxUnconsumed 未消耗的水平滚动距离
         * @param dyUnconsumed 未消耗的垂直滚动距离
         */
        void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed);

        /**
         * 嵌套滚动开始之前回调(先于子view)
         * @param target 发起嵌套滚动的子view
         * @param dx 消耗的水平滚动距离
         * @param dy 消耗的垂直滚动距离
         * @param consumed 输出此父view消耗的水平和垂直滚动距离
         */
        void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed);

        /**
         * 开始惯性滑动时回调(后于子view)
         * @param var1
         * @param var2
         * @param var3
         * @param var4
         * @return
         */
        boolean onNestedFling(@NonNull View var1, float var2, float var3, boolean var4);

        /**
         * 惯性滑动之前回调(先于子view)
         * @param var1
         * @param var2
         * @param var3
         * @return
         */
        boolean onNestedPreFling(@NonNull View var1, float var2, float var3);

        /**
         * 返回NestedScrollingParent的滑动方向
         * @return
         */
        int getNestedScrollAxes();
    }

ok,对两个接口的API有了一定的了解之后,现在我们来看一下NestedScrollingChild接口和NestedScrollingParent接口中的方法对应关系:

NestedScrollingChildImpl NestedScrollingParentImpl
startNestedScroll onStartNestedScroll, onNestedScrollAccepted
dispatchNestedPreScroll onNestedPreScroll
dispatchNestedScroll onNestedScroll
stopNestedScroll onStopNestedScroll

接下来写个实例验证一下,先上效果图:
NextedScrolling.gif
首先我们自定义一个YNestedScrollingChild继承自LinearLayout,然后实现NestedScrollingChild接口,并实现里面的方法,一般作为发起嵌套滑动的子View,只需自己实现onTouchEvent,在onTouchEvent里面处理相应的逻辑即可,其他NestedScrollingChild接口里面的方法就交给NestedScrollingChildHelper或它的super View去处理。

说明一点:为什么上面说可以交给super View处理呢?因为在API21或以上,安卓View里卖弄已经帮我们实现了NestedScrolling机制,即不需要我们手动实现NestedScrollingChild和NestedScrollingParent接口了,例如:

public class NestedScrollingChildImpl extends RelativeLayout {
   
    public NestedScrollingChildImpl(Context context
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值