Android事件冲突处理

在开发一个UI的时候,遇到这么一个需求:
有一个列表,列表中N各item;列表可以上下滑动,item可以左右滑动;当列表上下滑动时,及时左右滑动,item也不可以滑动;同理,当item左右滑动时,列表也不可以上下滑动。
最终效果图

在动手之前,现捋捋android事件的分发流程。在android的事件分发传递中,有dispathTouchEvent()onInterceptTouchEvent()onTouchEvent()三个方法:

  1. dispathTouchEvent()负责事件的分发,ViewGroupView中都有这个方法。
  2. onInterceptTouchEvent()负责事件的拦截,只有ViewGroup中有这个方法,默认返回false
  3. onTouchEvent()负责处理事件,ViewGroupView中都有该方法,当返回false时,说明不消耗事件,会返回到上层的onTouchEvent();当返回true时,会消耗改事件,后续的事件都会传递到该onTouchEvent()方法,而不会传递给中间ViewGrouponTouchEvent()方法。

具体的事件分发机制这里就不说了,网上有一大堆分析事件分发机制的文章,有兴趣的可以自行搜索,这里只大概说下事件的分发流程:

  1. 当屏幕接收到一个触摸事件的时候,会首先传递到ViewGroup
    dispatchTouchEvent()dispatchTouchEvent()会调用onInterceptTouchEvent()判断是否拦截该事件;

  2. onInterceptTouchEvent()返回false表示不拦截该事件,dispatchTouchEvent()会遍历子View,并根据触摸位置判断触摸的是哪个子View,然后调用该子ViewdispatchTouchEvent()方法,将事件传递下去;

  3. 如果onInterceptTouchEvent()返回true,表示拦截该事件,dispatchTouchEvent()就会调用onTouchEvent(),而不会将事件继续传递到子View去。

当子ViewACTION_DOWN事件返回了true后,后续事件中,如果onInterceptTouchEvent()返回true,拦截事件,则子View会立即接收到ACTION_CANCEL的事件。
如果事件传递到最低层的View后,还是返回false,则后续的一系列事件都不会再传递下来。

了解了事件的传递流程后,我们就能对上面的需求进行详细分析了:

  1. 因为父View和子View都会处理Touch事件,所以在ACTION_DOWN时需要在最底层的ViewonTouchEvent()返回true,表示接收处理事件。否则后续的事件都不会再传递进来,也就无法处理滑动了。
  2. View上下滑动,子View左右滑动,所以需要在ACTION_MOVE时,判断滑动方向。如果是上下滑动,则onInterceptTouchEvent()返回true,拦截该事件,事件交由自己的onTouchEvent()去处理并返回true;如果是左右滑动,则返回false,将事件交由子View处理。

判断滑动方向可以根据上下滑动速度和左右滑动速度比较得知。计算滑动速度需要借助VelocityTrackerVelocityTracker的具体用法可以自己进行搜索

核心拦截代码如下:

 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(TAG, TAG + " ------> onInterceptTouchEvent:" + ev.getAction());
        initVolocityTracker();
        mVelocityTracker.addMovement(ev);
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mStatus = STATUS_RESET;
                mVelocityTracker.clear();
                mVelocityTracker.addMovement(ev);
                y = ev.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                mVelocityTracker.computeCurrentVelocity(1000);
                float x = mVelocityTracker.getXVelocity();  //左右速度
                float y = mVelocityTracker.getYVelocity();  //上下速度
                //如果上下的速度大于左右的速度,则说明是上下滑动,需要拦截该事件
                if (mStatus == STATUS_RESET && Math.abs(y) > Math.abs(x)) {
                    mStatus = STATUS_INTERCEPT;
                    return true;
                } else if (x != 0 && y != 0){
                    mStatus = STATUS_UNINTERCEPT;
                }
                break;
        }

        return false;
    }

demo:TouchEventDemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值