Android 事件拦截分析

这一块的东西比较理解起来比较费劲,自己也是断断续续学习理解了三四次,才有点儿眉目。写出来,各位大神提点儿建议,助我爬坑。

测试代码,就是一个继承 Linearlayout 的父控件,重写onInterceptTouchEvent 和 onTouchEvent,然后就是一个继承 TextView 的子View 和一个继承 Button 的子View,两个子View 重写 onTouchEvent 方法。很简单,我在这儿就不贴了。

gif1

Android 事件拦截分析_第1张图片

一、看过不少博客,这一块儿东西无非就是三个方法。

1.1、父容器的分发事件方法,一般不会去重写

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    return super.dispatchTouchEvent(ev);
}

1.2、父容器的是否拦截事件的方法,经常重写

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);
    }

1.3、父容器和子控件的事件处理方法

@Override
public boolean onTouchEvent(MotionEvent event) {
    return super.onTouchEvent(event);
}

二、传递条件的重要性

2.1 看看正常情况下的传递逻辑(这点儿很重要)

2.1.1 子View为 TextView

我们直接贴 Log.

    onInterceptTouchEvent ACTION_DOWN
    MyTextView ACTION_DOWN
    parent ACTION_DOWN

只是触发了 Down 事件。为了方便大家理解,我们再画图描述一下。

Android 事件拦截分析_第2张图片

2.1.2 子View 为 Button

结果与TextView 是一样的。

2.1.3 子View 为注册了单击事件的 Button 操作见 gif1

 12-06 15:17:11.307 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_DOWN
12-06 15:17:11.308 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_DOWN
12-06 15:17:11.330 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 15:17:11.331 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 15:17:17.346 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 15:17:17.346 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 15:17:17.361 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 15:17:17.361 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 15:17:17.362 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_UP
12-06 15:17:17.362 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_UP
12-06 15:17:17.363 23478-23478/com.example.shoppingchen.touchdemo V/wangcong: dan ji le 

我们发现有些许的不一样了。 down move up 事件全部传递给了子View,但是没有调用父容器的 onTouchEvent 方法。

2.1.4 我们 Button 控件不注册 单击事件,而是把 onTouchEvent 中获取到 Down 事件时,返回 true.

发现结果和 2.1.3 是完全一样的(除了没有打出 dan ji le 字样)
所以,我们得出第一个结论:
结论1:消费事件的方法有两个,1、注册单击、长按等事件;2、onTouchEvent 返回 true。

2.1.5 我们在 父容器中ACTION_MOVE 事件代码做一些修改

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            i=0;
            android.util.Log.v("wangcong","onInterceptTouchEvent ACTION_DOWN");
            break;
        case MotionEvent.ACTION_MOVE:
            i++;
            // 在第三次传递 move 事件之后,我们进行拦截
            if(i > 3){
                return true;
            }
            android.util.Log.v("wangcong","onInterceptTouchEvent ACTION_MOVE");
            break;
        case MotionEvent.ACTION_UP:
            android.util.Log.v("wangcong","onInterceptTouchEvent ACTION_UP");
            break;
    }
    return super.onInterceptTouchEvent(ev);
}

看log:

12-06 15:41:18.420 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_DOWN
12-06 15:41:18.420 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_DOWN
12-06 15:41:18.438 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 15:41:18.438 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 15:41:18.455 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 15:41:18.455 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 15:41:18.488 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 15:41:18.488 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 15:41:18.505 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_CANCEL
12-06 15:41:18.523 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 15:41:18.539 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 15:41:18.555 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 15:41:18.572 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 15:41:18.572 2946-2946/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_UP

子View在接收三条 move 事件之后,就没有再接收到事件,拦截成功了。所以得出结论2.

结论2:在 onTouchEvent() 方法中拦截之后,其余事件就会一直继续被 该View 处理,除非上层拦截(即使你返回true,表示要处理这些事件,但是这些事件终究要通过上层传递给你,就是每次都要经过 onInterceptTouchEvent ,上层还是可以拦截)。

2.1.6 我们第一个 gif 图,手移动的范围没有超过子 View 的范围,那如果超过呢。

Android 事件拦截分析_第3张图片
结果是 与 2.1.3 一致。

2.1.7 那如果从外边滑动进入 Button 呢?

12-06 15:50:48.247 3975-3975/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_DOWN
12-06 15:50:48.248 3975-3975/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_DOWN

在这个时候,我们称之为容器的 View,其实就和我们的 Button 和 TextView 没有什么区别了。
那么得出结论:
结论3: 事件传递层次位置由 Down 事件确定,这就意味着只有与你按下位置相关的控件才有可能接收到事件,无关的绝对不可能接收到事件(除非你自己重新定义事件传递)。
结论4:所有这些控件都是继承 View,本质都是一样的。

三、事件的传递与拦截

3.1假如,我们有这样一个需求:

父容器不拦截,子控件消费 Down 事件,消费 3 个move 事件之后,返回false,不消费了。

12-06 16:06:35.097 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_DOWN
12-06 16:06:35.098 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_DOWN
12-06 16:06:35.108 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:06:35.108 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 16:06:35.142 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:06:35.142 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 16:06:35.158 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:06:35.159 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 16:06:35.192 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:06:37.309 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:06:37.326 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:06:37.358 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:06:37.361 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_UP
12-06 16:06:37.361 13085-13085/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_UP

我们看 log 发现,Move 事件接收了三个,但是up 事件还是传到了 子View 中。
得出结论:
结论5:down 返回false,则其它事件都不会再接收,down 事件返回true,即使 move 事件返回 false,也会接收到其他事件。

3.2 在结论2中,我们知道了,上层可以拦截下层的事件,那拦截之后呢?

我们修改容器onTouchEvent 方法,down 返回false,move 返回 true。 试一下

12-06 16:21:11.042 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_DOWN
12-06 16:21:11.043 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_DOWN
12-06 16:21:11.084 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:21:11.085 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 16:21:11.118 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:21:11.118 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 16:21:11.134 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: onInterceptTouchEvent ACTION_MOVE
12-06 16:21:11.134 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_MOVE
12-06 16:21:11.150 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: MyButtonView ACTION_CANCEL
12-06 16:21:11.167 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 16:21:11.184 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 16:21:11.200 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 16:21:11.217 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 16:21:11.234 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 16:21:11.246 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_MOVE
12-06 16:21:11.246 16510-16510/com.example.shoppingchen.touchdemo V/wangcong: parent ACTION_UP

从 log 中看出,先是 子 View down 事件返回 true , 表示要处理事件,接收四个 move 事件之后,父容器拦截了事件,然后父容器的 onTouchEvent 接收到事件,最终也接收到了Up事件。得出结论:
结论6:父容器可以拦截已经被子View 确定要处理的 事件,自己来处理。

3.3 到这儿之后我又有疑问了,那我父容器处理了三个 move 事件之后不想处理了,那能不能再给子 View 处理?

答案肯定是不行的,我们继续看 3.2 中的log,当父容器的 onTouchEvent 返回 true 决定处理后续事件之后,
onInterceptTouchEvent 没有再执行。
得出结论:
结论7:父容器能够给子容器擦屁股,但是子容器无法给父容器擦屁股。(话糙理不糙哈。)

这就是我对事件传递的一些理解了。
为什么要写这篇博客呢,因为感觉自己理解了和能否给别人讲清楚是两回事儿。如果有什么写的不对的地方,希望大家批评指正。

你可能感兴趣的:(Android,闲谈)