• Android事件分发机制


    事件分发机制

    Android事件分发是指在Android系统中,当用户触摸屏幕或执行其他操作时,系统如何将这些事件传递给正确的视图或组件进行处理的过程。

    Android事件分发遵循一种称为"事件分发机制"的规则,该机制由三个主要的阶段组成:触摸事件的捕获阶段、目标视图的处理阶段和冒泡阶段。

    在触摸事件的捕获阶段,事件从顶层视图(如Activity)开始,逐级向下传递,直到找到最底层的子视图。在这个过程中,每个视图都有机会拦截事件,如果某个视图拦截了事件,则后续的视图将无法接收到该事件。

    在目标视图的处理阶段,事件被传递给最底层的子视图,并由该视图进行处理。如果该视图没有处理事件,则事件将被传递给其父视图,直到找到能够处理事件的视图为止。

    在冒泡阶段,事件从底层视图向上冒泡,直到达到顶层视图。在这个过程中,每个视图都有机会处理事件,如果某个视图处理了事件,则后续的视图将无法接收到该事件。

    通过这种事件分发机制,Android系统能够准确地将用户的操作传递给正确的视图或组件进行处理,从而实现了用户与应用程序的交互。在实际开发中,我们可以通过重写视图的相关方法(如onTouchEvent())来自定义事件的处理逻辑,以满足特定的需求。

    涉及的对象

    1. View:View是Android中的基本UI组件,它负责接收用户的输入事件(如点击、触摸等),并将事件传递给相应的处理方法。

    2. ViewGroup:ViewGroup是View的子类,它可以包含其他的View或ViewGroup。当一个事件发生在ViewGroup上时,它会遍历其子View,并将事件传递给合适的子View。

    3. MotionEvent:MotionEvent是Android中的事件对象,它封装了用户的触摸事件信息,包括触摸点的坐标、触摸的动作等。

    4. MotionEventCompat:MotionEventCompat是一个兼容性类,用于处理不同Android版本之间的触摸事件兼容性问题。

    5. GestureDetector:GestureDetector是Android提供的手势检测器,它可以识别用户的手势操作,如滑动、长按、双击等。

    6. OnTouchListener:OnTouchListener是一个接口,用于监听View的触摸事件。通过实现该接口,可以自定义触摸事件的处理逻辑。

    以上是事件分发中的一些关键对象,它们共同协作,实现了Android中的事件分发机制。

    MotionEvent

    MotionEvent是Android中的一个类,用于处理与用户交互相关的事件,例如触摸屏幕、按下按钮等。它包含了一系列的常量和方法,用于获取事件的类型、坐标、时间等信息。通过监听MotionEvent,开发者可以实现对用户的触摸操作进行响应和处理。

    以下是一些常用的MotionEvent方法:

    • getAction():获取触摸事件的动作类型,返回一个整数值。可以通过使用MotionEvent.ACTION_DOWNMotionEvent.ACTION_MOVEMotionEvent.ACTION_UP等常量来判断触摸事件的具体类型。

    • getDownTime():获取当屏幕刚被按下时的时间(毫秒),按下后移动此时间不变

    • getEventTime():获取MotionEvent所在的事件被激发的时间(毫秒)

    • getX()getY():获取触摸事件的发生位置的横坐标和纵坐标。返回的是相对于触摸事件发生位置的坐标值。

    • getRawX()getRawY():获取触摸事件的发生位置的原始横坐标和纵坐标。返回的是相对于屏幕的坐标值。

    • getPointerCount():获取触摸事件中手指的数量。

    • getPointerId(int pointerIndex):获取指定索引的手指的ID。

    • getPressure(int pointerIndex):获取指定索引的手指的压力值。

    • getHistorySize():获取触摸事件的历史记录的数量。

    • getHistoricalX(int pointerIndex, int pos)getHistoricalY(int pointerIndex, int pos):获取指定索引的手指在指定历史记录位置的横坐标和纵坐标。

    这些方法可以帮助开发者获取触摸事件的相关信息,并进行相应的处理。

    MotionEvent#getAction()类型

    MotionEvent#getAction()方法返回一个整数,表示当前触摸事件的类型。具体的类型有以下几种:

    • ACTION_DOWN:手指按下屏幕时触发的事件
    • ACTION_MOVE:手指在屏幕上滑动时触发的事件
    • ACTION_UP:手指离开屏幕时触发的事件
    • ACTION_CANCEL:触摸事件被取消时触发的事件
    • ACTION_OUTSIDE:超出区域

    这些类型可以通过与MotionEvent类中定义的常量进行比较来判断当前触摸事件的类型。例如,可以使用以下代码来判断当前事件是否为按下事件:

    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        // 处理按下事件的逻辑
    }
    
    • 1
    • 2
    • 3

    示例:手指触摸屏幕到离开屏幕事件走向

    事件分发对象间传递

    在Android中,事件分发是通过View的事件分发机制来实现的。当用户触摸屏幕或者进行其他操作时,事件会从顶层的ViewGroup开始向下传递,直到找到合适的View来处理该事件。

    Android中的事件分发涉及到以下几个对象:

    1. Activity:Activity是Android应用程序的一个组件,它负责管理用户界面和处理用户交互。当用户触摸屏幕或者进行其他操作时,事件首先会传递给当前显示的Activity。

    2. Window:Window是Activity的一个属性,它表示一个窗口,用于显示Activity的用户界面。事件会首先传递给Window,然后由Window负责将事件传递给对应的View。

    3. View:View是Android中的基本UI组件,用于构建用户界面。每个View都有一个事件分发的责任,它可以处理自己感兴趣的事件,也可以将事件传递给其他View。

    4. ViewGroup:ViewGroup是View的子类,用于管理其他View的布局和显示。当事件传递到ViewGroup时,它会遍历自己的子View,并将事件传递给合适的子View。

    在事件分发过程中,每个对象都有机会处理事件。如果一个对象处理了事件,那么事件就会停止传递。如果一个对象没有处理事件,那么事件会继续向下传递,直到找到合适的处理者或者事件传递到最底层的View。

    可以通过重写View的dispatchTouchEvent()方法来实现事件的分发和传递。在该方法中,可以根据需要调用super.dispatchTouchEvent()方法将事件传递给父View或者调用View的onTouchEvent()方法来处理事件。

    Android中的事件分发是通过Activity、Window、View和ViewGroup等对象之间的协作来实现的。每个对象都有机会处理事件,通过合理地重写相关方法,可以实现事件的传递和处理。

    事件分发机制解析

    从上面的文章中我们得知Android事件分发机制的传递过程可以分为三个阶段:分发、拦截和处理。

    1. 分发阶段:事件首先由Activity或ViewGroup的dispatchTouchEvent()方法开始分发。在这个方法中,事件会被传递给当前ViewGroup的onInterceptTouchEvent()方法进行拦截判断。如果onInterceptTouchEvent()返回true,则表示当前ViewGroup拦截了事件,不再向下传递;如果返回false,则表示当前ViewGroup不拦截事件,会继续向下传递。

    2. 拦截阶段:如果当前ViewGroup不拦截事件,事件会继续向下传递给子View。子View的dispatchTouchEvent()方法会被调用,同样会经过onInterceptTouchEvent()方法的判断。如果子View拦截了事件,那么事件将不再向下传递给其他子View,而是交给子View的onTouchEvent()方法进行处理;如果子View不拦截事件,事件会继续向下传递。

    3. 处理阶段:如果事件没有被任何ViewGroup或View拦截,最终会传递给最底层的View的onTouchEvent()方法进行处理。在这个方法中,可以根据事件的类型(如触摸、滑动、点击等)进行相应的处理操作。

    Android事件分发机制的传递过程是从上到下的递归过程,事件会依次经过父ViewGroup和子View的拦截判断,最终到达最底层的View进行处理。这个过程中,可以通过重写相关方法来实现事件的拦截和处理,从而实现自定义的交互逻辑。

    Activity事件分发机制

    Activity的事件分发机制是通过ViewGroup和View的层级关系来实现的。当用户触摸屏幕或者按下按键时,系统会将事件传递给当前显示的Activity的根布局ViewGroup,然后由ViewGroup负责将事件分发给各个子View进行处理。

    具体的事件分发流程如下:

    1. 用户触摸屏幕或按下按键,事件首先传递给Activity的根布局ViewGroup。

    2. ViewGroup会调用自己的dispatchTouchEvent()方法来处理事件。在该方法中,ViewGroup会根据事件的类型(如触摸事件、按键事件等)进行相应的处理,例如判断是否需要拦截事件、是否需要消费事件等。

    3. 如果ViewGroup需要拦截事件,即认为自己应该处理该事件,那么事件就会停止向下传递,直接由ViewGroup来处理。

    4. 如果ViewGroup不需要拦截事件,那么事件会继续向下传递给子View。

    5. 子View会依次接收事件,并调用自己的dispatchTouchEvent()方法来处理事件。子View也会根据事件的类型进行相应的处理,例如判断是否需要拦截事件、是否需要消费事件等。

    6. 如果子View需要拦截事件,那么事件就会停止向下传递,直接由该子View来处理。

    7. 如果子View不需要拦截事件,那么事件会继续向下传递给下一个子View,直到事件被消费或者传递到最后一个子View。

    8. 如果事件最终没有被任何子View消费,那么事件会回到ViewGroup,ViewGroup会根据自身的情况来决定是否消费事件。

    9. 如果事件最终还是没有被消费,那么事件会继续向上层传递,直到被消费或者传递到最顶层的Activity。

    通过这样的事件分发机制,Android系统可以实现对用户的触摸和按键事件进行灵活的处理,从而实现各种交互效果。

    Activity源码:

    public boolean dispatchTouchEvent(MotionEvent ev) {
        // 开始事件都是Dwon,一般第一次都会进入到onUserInteraction
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        // 若Window返回true,则会告诉Activity也返回true。true在所有touch代表着终止,不再继续往下一个事件传递了
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    下面看getWindow().superDispatchTouchEvent(ev); Window是个抽象类,唯一实现类为PhoneWindow,定位到PhoneWindow的superDispatchTouchEvent()。

    PhoneWindow的源码:

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }
    
    • 1
    • 2
    • 3
    • 4

    mDecor是DecorView类,DecorView是PhoneWindow类的一个内部类。同时DecorView也是整个Window中的最顶层View。

    DecorView

    DecorView是Android中的一个类,它是Android系统中的顶级视图,用于承载应用程序的用户界面。它是Android窗口系统的一部分,负责管理应用程序的窗口和布局。

    DecorView是一个特殊的ViewGroup,它包含了应用程序的整个用户界面,包括状态栏、标题栏、内容区域等。它是Android应用程序的根视图,所有其他视图都是DecorView的子视图。

    DecorView的主要作用是提供一个容器,用于放置应用程序的布局和控件。它还负责处理用户输入事件,如触摸、滑动等,并将其传递给相应的子视图进行处理。

    在Android开发中,我们通常不直接操作DecorView,而是通过ActivityFragment来管理和操作应用程序的用户界面。DecorView在内部被ActivityFragment自动创建和管理,开发者只需要关注布局和控件的设计和交互逻辑即可。

    DecorView是Android应用程序的根视图,负责承载应用程序的用户界面,并提供容器和事件处理功能。

    DecorView是一个特殊的ViewGroup,分发处理同ViewGroup,下面看ViewGroup的superDispatchTouchEvent()

    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }
    
    • 1
    • 2
    • 3

    整个Activity事件分发过程如下图:

    ViewGroup事件分发机制

    ViewGroup是一种特殊的View,它可以包含其他的View或者ViewGroup。当用户进行触摸操作时,事件会被传递给ViewGroup,并由ViewGroup负责将事件分发给其子View或者子ViewGroup。

    ViewGroup的事件分发机制主要包括以下几过程:

    1. 事件的传递:当用户触摸屏幕时,事件会首先传递给最顶层的ViewGroup,即Activity的根布局。然后,ViewGroup会根据触摸事件的坐标位置,确定哪个子View或者子ViewGroup应该接收该事件。

    2. 事件的拦截:在确定了接收事件的子View或者子ViewGroup后,ViewGroup会调用该子View或者子ViewGroup的dispatchTouchEvent()方法,将事件传递给它们。在这个过程中,如果父ViewGroup需要拦截事件,可以通过重写onInterceptTouchEvent()方法来实现。如果父ViewGroup拦截了事件,那么该事件将不会传递给子View或者子ViewGroup,而是由父ViewGroup来处理。

    3. 事件的处理:当事件传递到子View或者子ViewGroup时,它们会调用自己的dispatchTouchEvent()方法来处理事件。在这个方法中,子View或者子ViewGroup可以根据自己的需求来处理事件,例如响应点击、滑动等操作。如果子View或者子ViewGroup不消费事件,那么事件会继续传递给父ViewGroup,直到事件被消费或者传递到最顶层的ViewGroup。

    ViewGroup的事件分发机制是通过事件的传递、拦截和处理来实现的。通过重写dispatchTouchEvent()和onInterceptTouchEvent()方法,可以对事件进行自定义的处理。这种机制可以保证事件在ViewGroup及其子View或者子ViewGroup之间的正确传递和处理。

    整个ViewGroup分发过程如下图:

    View事件分发机制

    View事件分发机制主要包括三个关键方法:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。

    1. dispatchTouchEvent:该方法是ViewGroup类中的一个关键方法,用于分发触摸事件。当一个触摸事件发生时,系统会首先将该事件传递给顶层的ViewGroup,然后由ViewGroup根据一定的规则将事件传递给子View进行处理。在dispatchTouchEvent方法中,系统会根据事件类型和触摸位置等信息,决定将事件传递给哪个子View进行处理。

    2. onInterceptTouchEvent:该方法也是ViewGroup类中的一个关键方法,用于拦截触摸事件。当一个触摸事件传递给ViewGroup后,ViewGroup会先调用onInterceptTouchEvent方法来判断是否需要拦截该事件。如果onInterceptTouchEvent方法返回true,则表示ViewGroup会拦截该事件,并将事件传递给自己的onTouchEvent方法进行处理;如果返回false,则表示ViewGroup不会拦截该事件,会将事件继续传递给子View进行处理。

    3. onTouchEvent:该方法是View类中的一个关键方法,用于处理触摸事件。当一个触摸事件传递到View时,View会调用自己的onTouchEvent方法来处理该事件。在onTouchEvent方法中,可以根据事件类型进行相应的处理,例如处理点击事件、滑动事件等。

    View事件分发机制的流程如下:

    1. 当用户触摸屏幕时,系统会将触摸事件传递给顶层的ViewGroup。
    2. ViewGroup会调用dispatchTouchEvent方法来分发触摸事件。
    3. 在dispatchTouchEvent方法中,ViewGroup会根据一定的规则将事件传递给子View进行处理。
    4. 子View会先调用onInterceptTouchEvent方法来判断是否需要拦截该事件。
    5. 如果onInterceptTouchEvent方法返回true,则表示ViewGroup会拦截该事件,并将事件传递给自己的onTouchEvent方法进行处理。
    6. 如果onInterceptTouchEvent方法返回false,则表示ViewGroup不会拦截该事件,会将事件继续传递给子View进行处理。
    7. 子View会调用自己的onTouchEvent方法来处理触摸事件。

    View#dispatchTouchEvent源码:

    public boolean dispatchTouchEvent(MotionEvent event) {
        ...
        if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    只有当4个条件都为真才返回true,否则执行onTouchEvent(),下面对这4个条件逐个分析:

    • li != null:li即是ListenerInfo,ListenerInfo是封装了所有事件,所以只要赋值任一事件,这个都不可能会为null。

    • mOnTouchListener != null:mOnTouchListener变量在View.setOnTouchListener()方法里赋值,即只要我们给控件注册了Touch事件,mOnTouchListener就不为空。

    • (mViewFlags & ENABLED_MASK) == ENABLED:判断当前点击的控件是否enable,由于View默认enable,故该条件为true。

    • mOnTouchListener.onTouch(this, event):控件注册touch事件时重写的onTouch()方法。

    若在setOnTouchListener返回true,就会满足以上4个条件,并且返回了true,从而使得View.dispatchTouchEvent()直接返回true,事件分发结束,不会执行onTouchEvent(event)。

    View#onTouchEvent(event)源码:

    public boolean onTouchEvent(MotionEvent event) {
        ...
        // clickable代表该控件是否可点击,可点击就进入下面条件判断
        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {
                // 1. 当前的事件 = 抬起View
                case MotionEvent.ACTION_UP:
                    // 经过种种判断,此处省略
                    ........
                    if (!focusTaken) {
                        // 执行performClick()
                        if (mPerformClick == null) {
                            mPerformClick = new PerformClick();
                        }
                        if (!post(mPerformClick)) {
                            performClickInternal();
                        }
                    }
                    break;
                    // 2. 当前的事件 = 按下View
                    case MotionEvent.ACTION_DOWN:
                        // 经过种种判断,此处省略
                        break;
                    // 3. 当前的事件 = 结束事件(非人为原因)
                    case MotionEvent.ACTION_CANCEL:
                        // 经过种种判断,此处省略
                        break;
                    // 4. 当前的事件 = 滑动View
                    case MotionEvent.ACTION_MOVE:
                        // 经过种种判断,此处省略
                        break;
                }
                // 若该控件可点击,就一定返回true
                return true;
            }
        // 若该控件不可点击,就一定返回false
        return false;
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    整个View分发过程如下图:

    总结

    dispatchTouchEvent

    dispatchTouchEvent用于分发触摸事件。它是ViewGroup类中的一个方法,用于将触摸事件传递给子View或处理自身的触摸事件。

    触摸事件的传递是通过触摸事件分发机制来实现的。当用户触摸屏幕时,系统会将触摸事件传递给顶层的ViewGroup,然后由ViewGroup负责将触摸事件传递给子View或处理自身的触摸事件。

    dispatchTouchEvent方法的作用是将触摸事件分发给子View或处理自身的触摸事件。它会根据触摸事件的类型和位置来确定是将触摸事件传递给子View,还是处理自身的触摸事件。

    dispatchTouchEvent方法中,会依次调用onInterceptTouchEvent方法和onTouchEvent方法来判断是否拦截触摸事件和处理触摸事件。如果onInterceptTouchEvent方法返回true,则表示拦截触摸事件,不再向子View传递触摸事件;如果onTouchEvent方法返回true,则表示处理了触摸事件,不再向子View传递触摸事件。

    onTouchEvent

    onTouchEvent用于处理触摸事件。它是View类的一个成员方法,可以被重写以实现自定义的触摸事件处理逻辑。

    触摸事件包括按下(ACTION_DOWN)、移动(ACTION_MOVE)、抬起(ACTION_UP)等多个动作。当用户触摸屏幕时,系统会将触摸事件传递给相应的View,并调用该View的onTouchEvent方法来处理事件。

    onTouchEvent方法中,可以根据不同的触摸动作进行相应的处理,例如根据触摸位置进行绘制、处理滑动事件、处理点击事件等。可以通过重写onTouchEvent方法来实现自定义的触摸交互效果。

    重写onTouchEvent方法来处理触摸事件:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                // 处理按下事件
                break;
            case MotionEvent.ACTION_MOVE:
                // 处理移动事件
                break;
            case MotionEvent.ACTION_UP:
                // 处理抬起事件
                break;
        }
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    需要注意的是,onTouchEvent方法的返回值为boolean类型。如果返回true,表示已经处理了该触摸事件,不再向其他View传递;如果返回false,则会将该触摸事件传递给父View或其他相关的View进行处理。

    onInterceptTouchEvent

    onInterceptTouchEvent用于拦截触摸事件。它通常用于父容器对子View的触摸事件进行拦截和处理。

    触摸事件是由屏幕上的触摸点产生的,包括按下、移动和抬起等动作。当一个触摸事件发生时,系统会将该事件传递给最上层的View,并通过dispatchTouchEvent方法进行分发。在分发过程中,如果父容器的onInterceptTouchEvent方法返回true,则表示父容器要拦截该事件,不再将事件传递给子View;如果返回false,则表示父容器不拦截该事件,继续将事件传递给子View。

    onInterceptTouchEvent方法的返回值决定了是否拦截触摸事件,它有三种可能的返回值:

    • 返回true:表示父容器要拦截触摸事件,不再传递给子View。
    • 返回false:表示父容器不拦截触摸事件,继续传递给子View。
    • 返回super.onInterceptTouchEvent(event):表示父容器不对触摸事件进行拦截,继续按照默认的方式处理。

    通过在onInterceptTouchEvent方法中对触摸事件进行处理,我们可以实现一些特定的触摸事件逻辑,例如滑动冲突处理、多指触摸事件的处理等。

    setOnTouchListener

    setOnTouchListener是一个用于设置触摸事件监听器的方法,用于对触摸事件进行处理。

    使用setOnTouchListener方法,可以为一个控件(如Button、ImageView等)设置一个触摸事件监听器。当用户触摸该控件时,触摸事件监听器会被触发,并执行相应的操作。

    示例代码:

    button.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // 处理触摸事件的逻辑代码
            return true; // 返回true表示已经处理了触摸事件,false表示未处理
        }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    button是要设置触摸事件监听器的视图对象。setOnTouchListener方法接受一个View.OnTouchListener对象作为参数,该对象实现了onTouch方法,用于处理触摸事件。

    onTouch方法中,可以编写自定义的触摸事件处理逻辑。根据MotionEvent对象的不同动作(如按下、移动、抬起等),可以执行相应的操作。最后,需要返回一个布尔值,表示是否已经处理了触摸事件。

    使用setOnTouchListener方法可以实现各种触摸事件的处理,例如拖动、缩放、滑动等。根据具体需求,可以在onTouch方法中编写相应的代码逻辑。

  • 相关阅读:
    java计算机毕业设计流浪动物救助站系统源码+系统+mysql数据库+lw文档
    设备搭建(waf、蜜罐、ids和ips)
    Github每日精选(第73期):Go 的 JSON 解析器gjson
    Oracle AutoVue 21.0.x最新支持程序文件格式及版本
    python+django高校教师科研成果管理系统pycharm源码lw
    .NET混合开发解决方案4 WebView2的线程模型
    hero博客搭建
    [源码解析] TensorFlow 分布式环境(7) --- Worker 动态逻辑
    python的网络请求库urllib、urllib2、urllib3、request的联系
    linux系统学习笔记9——CANOpen状态转换
  • 原文地址:https://blog.csdn.net/qq_35805528/article/details/133714808