Android事件分发机制

简介

Android事件分发机制,可以算是面试时的常客。可是很多人对其流程只有一个大致的了解,并未对各种情况进行跟踪观察,这也导致对触摸事件的应用,只处于低级阶段。

1、默认情况下事件传递流程:

1、无组件消费Down事件

dispatchTouchEvent返回super.dispatchTouchEvent()

onInterceptTouchEvent返回false

onTouchEvent返回false

2、有组件消费Down事件

dispatchTouchEvent返回super.dispatchTouchEvent()

onInterceptTouchEvent返回false

有组件onTouchEvent返回true

2、Down事件在dispatchTouchEvent返回true

有组件dispatchTouchEvent返回true

onInterceptTouchEvent返回false

onTouchEvent返回false

3、Down事件在dispatchTouchEvent返回false

1、无组件消费Down事件

有组件dispatchTouchEvent返回false onInterceptTouchEvent返回false onTouchEvent返回false

2、有组件消费Down事件

有组件dispatchTouchEvent返回false

onInterceptTouchEvent返回false

有组件onTouchEvent返回true

4、有控件拦截Down事件:

1、无组件消费Down事件

dispatchTouchEvent返回super.dispatchTouchEvent()

onInterceptTouchEvent返回true

onTouchEvent返回false

2、有组件消费Down事件

dispatchTouchEvent返回super.dispatchTouchEvent()

onInterceptTouchEvent返回true

有组件onTouchEvent返回true

5、ACTION_DOWN消息总结:

1、若在dispatchTouchEvent和onTouchEvent函数中返回true,将会截断消息,后续节点将不会收ACTION_DOWN 消息。

2、onInterceptTouchEvent函数只会改变ACTION_DOWN消息的正常流向,消息会直接流向自己的onTouchEvent函数中,并不会截断消息。

3、若在 dispatchTouchEvent函数中返回false拦截消息,同样会改变ACTION_DOWN消息的正常流向,消息会直接流向其父控件的onTouchEvent函数中,同样不会截断消息。

4、一般我们在拦截消息时,都是共同使用onInterceptTouchEvent和onTouchEvent函数的,通过在 onInterceptTouchEvent函数中返回 true,将 ACTION_DOWN消息流向自己的onTouchEvent函数中,然后在该onTouchEvent函数中返回 true拦截消息。

6、ACTION_MOVE消息总结:

1、在 dispatchTouchEvent 函数中返回 true拦截消息后,ACTION_MOVE 消息的流向与ACTION_DOWN消息的完全相同,消息会直接停止传递,后面的子控件都不会接收到这个消息。

2、无论ACTION_DOWN消息的流向是怎样的,只要最终流到onTouchEvent函数中就行。假设控件A最终在onTouchEvent 函数中消费了 ACTION_DOWN消息,那么ACTION_MOVE 消息的流向就是先流到控件A的 dispatchTouchEvent函数中,最终直接流到控件A的onTouchEvent 函数中,进而消息停止传递。

在ACTION_MOVE消息到来时拦截

为了在ACTION_MOVE消息到来时进行拦截事件,就必须让ACTION_DOWN消息被自身或子控件的onTouchEvent函数消费。

1、在onInterceptTouchEvent拦截Move事件

java 复制代码
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case ACTION_MOVE:
            return true;
    }
    return super.onInterceptTouchEvent(event);
}

上图看起来不容易理解,利用4条线来帮助大家理解。

红线:这条线表示ACTION_DOWN消息的传递流程,也就是上面讲解的在TextView的onTouchEvent函数中消费ACTION_DOWN 消息的传递流程。

绿线:这条线表示ACTION_MOVE消息第一次传递时的流向情况。本来消息依然会从Activity的dispatchTouchEvent函数流向子控件,但是在到达ViewGroupl的onInterceptTouchEvent 函数时,消息被拦截了。到这里,这次的ACTION_MOVE 消息就没有了,变成了ACTION_CANCEL消息继续向子控件传递,一直传递到ACTION_MOVE消息原本要传递的位置,通知所有被截断的子控件,它们的消息取消了,后面没有消息再传递过来。当我们收到ACTION_CANCEL消息时,就表示后续不会再获得消息,一般需要像处理ACTION_UP消息一样处理该消息,执行控件归位等操作。

蓝线:这条线表示消息被截断之后的ACTION_MOVE、ACTION_UP消息的流向。可以看到,这时候的ACTION_MOVE 消息的流向与正常情况下ViewGroup1的 dispatchTouchEvent函数拦截ACTION_DOWN消息时ACTION_MOVE消息的流向是完全相同的。在这里,我没有在ViewGroup1的onTouchEvent 函数中进行返回true的消息拦截,所以消息最终会流到Activity的onTouchEvent函数中。 需要特别注意的是,虽然ACTION_MOVE消息最终会流到Activity的onTouchEvent函数中,但后续的 ACTION_MOVE 消息并不会像正常处理流程一样(可以查看黑线,从 Activity的 dispatchTouchEvent 函数直接流到Activity 的 onTouchEvent函数中),而是每次ACTION_MOVE 消息的流向都与绿线保持一致,这就说明在这种情况下即使所有控件的onTouchEvent 函数都不拦截消息,ACTION_MOVE消息依然会走完全程。 其实这一点非常好理解,因为我们是在 ViewGroup1的 ACTION_MOVE消息到来时进行拦截的,而对于它的父控件,这里是Activity,并不知道这件事,它只会按照正常流程下ACTION_MOVE消息的流程来传递消息,所以每次ACTION_MOVE消息都会流到ViewGroup1中,只是ViewGroup1进行了拦截而已。

2、在dispatchTouchEvent拦截Move事件

2.1、返回true

java 复制代码
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case ACTION_MOVE:
            return true;
    }
    return super.dispatchTouchEvent(event);
}

红线:表示ACTION_DOWN消息传递流程。

绿线:表示ACTION_MOVE消息传递流程,在dispatchTouchEvent中被截断,子控件不会收到ACTION_CANCEL事件。

蓝线:表示ACTION_UP消息传递流程,这种情况下的ACTION_UP和正常情况下ACTION_UP消息传递流程相同。

2.2、返回false

java 复制代码
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case ACTION_MOVE:
            return false;
    }
    return super.dispatchTouchEvent(event);
}

如果dispatchTouchEvent拦截MOVE事件返回true时,事件将直接流到Activity的onTouchEvent中。

3、详解requestDisallowInterceptTouchEvent

requestDisallowInterceptTouchEvent主要用途是告诉父类,不要拦截消息,即不再调用onInterceptTouchEvent。

当我们通过requestDisallowInterceptTouchEvent,来禁止父控件拦截消息时,该控件的所有父控件的onInterceptTouchEvent函数都将被跳过。

常见滑动冲突场景

1、外层与内层的滑动方向不一致 根据方向阶段滑动那个View

2、外层与内层的滑动方向一致 只有一种解决方法:根据业务需求,通过下面的拦截和禁止方法,决定在什么情况下滑动那个View。

1、外部拦截法 需要重写父控件的onInterceptTouchEvent函数。

2、内部拦截法 父控件不拦截任何消息,所有消息都传递给子控件,如果子控件需要此消息就直接消费掉,否则就交给父控件来处理。利用requestDisallowInterceptTouchEvent实现的。

相关推荐
太空漫步111 小时前
android社畜模拟器
android
海绵宝宝_4 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
凯文的内存6 小时前
android 定制mtp连接外设的设备名称
android·media·mtp·mtpserver
天若子6 小时前
Android今日头条的屏幕适配方案
android
林的快手7 小时前
伪类选择器
android·前端·css·chrome·ajax·html·json
望佑8 小时前
Tmp detached view should be removed from RecyclerView before it can be recycled
android
xvch10 小时前
Kotlin 2.1.0 入门教程(二十四)泛型、泛型约束、绝对非空类型、下划线运算符
android·kotlin
人民的石头14 小时前
Android系统开发 给system/app传包报错
android
yujunlong391914 小时前
android,flutter 混合开发,通信,传参
android·flutter·混合开发·enginegroup
rkmhr_sef14 小时前
万字详解 MySQL MGR 高可用集群搭建
android·mysql·adb