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实现的。

相关推荐
沐言人生3 小时前
Android10 Framework—Init进程-9.服务端属性值初始化
android·android studio·android jetpack
追光天使3 小时前
【Mac】和【安卓手机】 通过有线方式实现投屏
android·macos·智能手机·投屏·有线
小雨cc5566ru3 小时前
uniapp+Android智慧居家养老服务平台 0fjae微信小程序
android·微信小程序·uni-app
一切皆是定数4 小时前
Android车载——VehicleHal初始化(Android 11)
android·gitee
一切皆是定数4 小时前
Android车载——VehicleHal运行流程(Android 11)
android
problc4 小时前
Android 组件化利器:WMRouter 与 DRouter 的选择与实践
android·java
图王大胜5 小时前
Android SystemUI组件(11)SystemUIVisibility解读
android·framework·systemui·visibility
服装学院的IT男9 小时前
【Android 13源码分析】Activity生命周期之onCreate,onStart,onResume-2
android
Arms2069 小时前
android 全面屏最底部栏沉浸式
android
服装学院的IT男9 小时前
【Android 源码分析】Activity生命周期之onStop-1
android