Android-事件分发-嵌套滑动

前言

在前期做一些Android酷炫效果并遇到嵌套滑动问题的算是一大难点,没有标准的规范,开发人员根据自己的需求进行随意控制,导致做的一些组件缺少复用性,同时也不利于后期的维护。后期官方出了自己的嵌套滑动标准,主要由NestedScrollingChild、NestedScrollingChildHelper、NestedScrollingParent、NestedScrollingParentHelper进行控制,从而规范了嵌套滑动事件的处理标准。

下图展示的一个嵌套滑动的效果图,这里我们定义:

  1. A:表示蓝色部分视图,一个ViewGroup
  2. B:表示淡绿色部分视图,自定义的一个View

后面的描述我们都将用A和B表示。

image

们需要达到的效果是当触摸到B的时候,B滑动一段距离,然后再滑动A;接下来我们已两种不同的方式来实现该效果。

如何实现

方案一:使用传统的事件分发机制进行控制

要实现该效果,那么我们就需要在B中消费事件,同时满足一定条件后控制父视图A的滑动;这里我们定义一个view,重写onToucheEvent()进行事件消费处理:

public class DemoView extends View {
    //最大滑动距离-需要根据自己需要进行控制
    private int maxMoveDistance = 200;
    public DemoView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }
}

这里的onTouchEvent是我们处理的关键,因此详细介绍具体实现过程:

第一步: 处理Down事件

//做横向滑动,记录坐标即可
private int lastTouchX;
@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            lastTouchX = (int) event.getRawX();
            ViewParent viewParent = getParent();
            if(viewParent != null){
                //请求父视图不拦截事件
                viewParent.requestDisallowInterceptTouchEvent(true);
            }
            break;
    }
    return super.onTouchEvent(event);
}

上面的代码已有注释,我们取出点击时的坐标,同时要求父View不拦截事件.

第二步: 处理Move事件

View的移动我们通过setTranslationX的方式来控制,移动的距离为当前移动X坐标与上一次按下或者移动的X坐标的差,即event.getRawX()-lastTouchX;在滑动的过程中,我们需要判断当前View达到相应临界点消费的滑动距离,没有消费完成则由父View消费,整个onTouch的代码如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            lastTouchX = (int) event.getRawX();
            ViewParent viewParent = getParent();
            if (viewParent != null) {
                //请求父视图不拦截事件
                viewParent.requestDisallowInterceptTouchEvent(true);
            }
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            ViewParent viewParent = getParent();
            if (viewParent != null) {
                //请求父视图不拦截事件
                viewParent.requestDisallowInterceptTouchEvent(true);
            }

            int dx = (int) (event.getRawX() - lastTouchX);
            //判断当前View能够移动的最大距离
            int oldViewX = (int) getTranslationX();
            int viewTranslationX = oldViewX + dx;
            if (viewTranslationX < 0) {
                setTranslationX(0);
            } else if (viewTranslationX > maxMoveDistance) {
                setTranslationX(maxMoveDistance);
            } else {
                setTranslationX(viewTranslationX);
            }
            //判断当前View是否已经消费完滑动的距离,没有消费完则交由父View处理
            int dxConsumed = (int) (getTranslationX() - oldViewX);
            int dxUnConsumed = dx - dxConsumed;
            if (dxUnConsumed != 0) {
                if (viewParent != null) {
                    ViewGroup viewGroup = (ViewGroup) viewParent;
                    viewGroup.setTranslationX(viewGroup.getTranslationX() + dxUnConsumed);
                }
            }
            lastTouchX = (int) event.getRawX();
            break;
        }
    }
    return true;
}

第三步: 验证结果

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="100dp">

    <View
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:background="@color/colorPrimary"></View>

    <com.water.view.demo.DemoView
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值