• Android事件分发机制


    Android事件分发机制

    为什么会有事件分发机制

    android上面的view是树形结构的,view可能会重叠在一起,当我们点击的地方有过个view都可以响应的时候,这个点击事件应该交给谁来处理,就需要事件分发机制。

    1.概述

    事件分发的三个重要方法

    public boolean dispatchTouchEvent(MotionEvent event)
    public boolean onInterceptTouchEvent(MotionEvent event)
    public boolean onTouchEvent(MotionEvent event)
    

    MotionEvent取值如下:

        public static final int ACTION_DOWN             = 0;
        public static final int ACTION_UP               = 1;
        public static final int ACTION_MOVE             = 2;
        public static final int ACTION_CANCEL           = 3;
    

    ACTION_DOWN 表示手指按下时发出的消息
    ACTION_UP 表示手指抬起时发出的消息
    ACTION_MOVE 表示手指移动时发出的消息
    ACTION_CANCEL 表示结束事件时手指发出的消息

    举个例子讲解事件分发机制:

    布局为Activity嵌套两个ViewGroup,最顶层的ViewGroup里面套一个TextView

    如下不拦截消息时,ACTION_DOWN消息的完整传递过程为:

    image

    注意:
    对于Activity来说不需要 onInterceptTouchEvent 方法,因为拦截没有意义,拦截后整个view树都点击不了。
    对于view来说,事件要么消费,要么回传,也没有拦截的必要。

    因此在Activity,ViewGroup,TextView中都有disPatchTouchEvent方法和onTouchEvent方法。
    对于ViewGroup来说,多了一个onInterceptTouchEvent方法。

    先说不包含 onInterceptTouchEvent 方法时消息传递过程,再来看包含onInterceptTouchEvent方法时消息传递过程

    2. 不包含 onInterceptTouchEvent 方法时 ACTION_DOWN 消息传递过程

    什么是拦截?看下面这三个方法都有一个boolean类型的返回值。默认返回false表示没有拦截,即自己不做处理。返回true时,表示自己处理这个事件,就是拦截了。

    public boolean dispatchTouchEvent(MotionEvent event)
    public boolean onInterceptTouchEvent(MotionEvent event)
    public boolean onTouchEvent(MotionEvent event)
    

    2.1 dispatchTouchEvent 返回值简介

    dispatchTouchEvent 方法对于返回值的处理比较特殊,默认的处理方法并不是返回false,而是直接调用super.dispatchTouchEvent.这种处理方式与直接返回false不同。因此对于dispatchTouchEvent,有三种返回值:

    • 默认的super.dispatchTouchEvent
    • 返回true
    • 返回false

    2.2 在 dispatchTouchEvent 方法中返回 true 拦截消息

    2.2.1 在 Activity 的 dispatchTouchEvent 方法中拦截消息

    在 Activity 的 dispatchTouchEvent 方法中拦截消息后,消息会直接断掉,不会往任何地方传递,只有 Activity 的 dispatchTouchEvent 方法收到 ACTION_DOWN 消息。

    2.2.2 在 ViewGroup 的 dispatchTouchEvent 方法中拦截消息

    在 ViewGroup 的 dispatchTouchEvent 方法中拦截消息后,ACTION_DOWN 消息同样会直接停止传递,其他任何子控件都收不到消息。

    2.2.2 在 View 的 dispatchTouchEvent 方法中拦截消息

    在传递到 View 的 dispatchTouchEvent 方法后,消息就停止传递了。

    因此,无论在哪个控件的 dispatchTouchEvent 方法中拦截消息,消息都会直接停止传递,后面的子控件都不会接收到这个消息。

    2.3 在 dispatchTouchEvent 方法中返回 false 拦截消息

    先说结论:在 dispatchTouchEvent 方法中返回 false 拦截消息后,消息不会直接停止传递,而是向父控件的 onTouchEvent 方法回传。

    2.3.1 在 View 的 dispatchTouchEvent 方法中返回 false

    2.3.2 在 ViewGroup2 的 dispatchTouchEvent 方法中返回 false

    2.3.3 在 ViewGroup1 的 dispatchTouchEvent 方法中返回 false

    2.3.4 在 Activity 的 dispatchTouchEvent 方法中返回 false

    从上面这些消息传递过程中可以看出,在控件的 dispatchTouchEvent 方法中返回 false 后, ACTION_DOWN 消息会在 dispatchTouchEvent 这条线上停止传递,并向其父控件的 onTouchEvent 方法回传。

    2.4 在 onTouchEvent 方法中拦截 ACTION_DOWN 消息

    onTouchEvent 方法中,默认返回 false,表示不拦截; 当返回 true 时, 表示拦截。

    先说结论:
    无论哪个控件的 onTouchEvent 方法拦截 ACTION_DOWN 消息, 消息都会直接停止传递, 后面的父控件都不会接收到这个消息。

    总结:

    • dispatchTouchEvent 和 onTouchEvent 方法一旦返回true拦截消息, ACTION_DOWN 消息就会停止传递,正常流程下的后续节点都不会收到 ACTION_DOWN 消息了。
    • 当 dispatchTouchEvent 方法,返回 false 时,首先会拦截 ACTION_DOWN 消息,消息不会继续传给子控件,而是传给父控件的 onTouchEvent,然后继续回传。

    3. 在 onInterceptTouchEvent 方法中的 ACTION_DOWN 消息传递流程

    3.1 onInterceptTouchEvent 方法的作用

    首先,只有 ViewGroup 具有 onInterceptTouchEvent 方法, Activity 和 View 中都没有这个方法。

    然后, onInterceptTouchEvent 方法其实就是一个拦截过滤器。每个 ViewGroup 每次在处理消息分发时,都会问一问拦截器要不要拦截(也就是这个消息要不要自己处理)。要处理就返回true,然后交给自己的 onTouchEvent 方法处理。如果不拦截,就继续往子控件传递。默认是不拦截的。

    3.2 在 ViewGroup 的 onInterceptTouchEvent 方法中拦截 ACTION_DOWN 消息

    3.2.1 仅在 ViewGroup2 的 onInterceptTouchEvent 方法中拦截 ACTION_DOWN 消息

    3.2.2 仅在 ViewGroup1 的 onInterceptTouchEvent 方法中拦截 ACTION_DOWN 消息

    3.2.3 在 ViewGroup2 的 onInterceptTouchEvent 和 onTouchEvent 方法中拦截 ACTION_DOWN 消息

    总结:

    在 ViewGroup 的 onInterceptTouchEvent 方法中拦截 ACTION_DOWN 消息,会改变 ACTION_DOWN 消息的流向,让它直接流向当前 ViewGroup 的 onTouchEvent 方法。
    同样,如果 onTouchEvent 方法没有拦截继续拦截消息,则消息会继续传递。如果某个 onTouchEvent 方法中拦截消息的话, ACTION_DOWN 会直接停止传递。

    4. 关于 ACTION_MOVE 和 ACTION_UP 消息传递流程

    上面讲解的都是 ACTION_DOWN 消息的传递流程,我们知道 ACTION_DOWN 之后,是 ACTION_MOVE, 最后是 ACTION_UP 。
    这两者与 ACTION_DOWN 的消息传递并不完全一样。

    因为 ACTION_MOVE 和 ACTION_UP 的消息流向是完全一样的,所以只讲 ACTION_MOVE。

    4.1 在 dispatchTouchEvent 方法中返回 true 的消息传递流程

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

    4.1 在 onTouchEvent 方法中返回 true 的消息传递流程

    这部分很复杂,稍后补充。

    ACTION_MOVE 消息总结:

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

    • 无论 ACTION_DOWN 的最终流向如何,只要最终流到 onTouchEvent 方法中就行。假设控件 A 最终在 onTouchEvent 方法中消费了 ACTION_DOWN 消息,那么 ACTION_MOVE 消息的流向就是先流到控件 A 的 dispatchTouchEvent 方法中,最终直接流到控件 A 的 onTouchEvent 方法中。进而消息停止传递。

    事件传递流程

    Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> view

    典型的责任链模式

    如果最后的view也没有消费事件,那么事件就会被依次向上回传直到Activity,如果Activity也没有处理该事件,那么事件就被抛弃。
    既保证了事件的有序性,又非常的灵活。

    参考文献

    《Android自定义控件高级进阶与精彩实例》

  • 相关阅读:
    HDU
    HDU
    CodeForces
    HDU——2955 Robberies (0-1背包)
    南京区域赛之后
    算法作业三-哈夫曼编码
    HDU
    POJ 1220 NUMBER BASE CONVERSION(进制转换,大数)
    HDU 1535 Invitation Cards(最短路)
    HDU 3572 Task Schedule(网络流+当前弧优化)
  • 原文地址:https://www.cnblogs.com/cfdroid/p/16349966.html
Copyright © 2020-2023  润新知