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类中定义的常量进行比较来判断当前触摸事件的类型。例如,可以使用以下代码来判断当前事件是否为按下事件:

java 复制代码
if (event.getAction() == MotionEvent.ACTION_DOWN) {
    // 处理按下事件的逻辑
}

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

事件分发对象间传递

在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源码:

java 复制代码
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);
}

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

PhoneWindow的源码:

java 复制代码
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

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()

java 复制代码
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}

整个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源码:

java 复制代码
public boolean dispatchTouchEvent(MotionEvent event) {
    ...
    if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) {
        result = true;
    }
    ...
}

只有当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)源码:

java 复制代码
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;
    ...
}

整个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方法来处理触摸事件:

java 复制代码
@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;
}

需要注意的是,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等)设置一个触摸事件监听器。当用户触摸该控件时,触摸事件监听器会被触发,并执行相应的操作。

示例代码:

java 复制代码
button.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // 处理触摸事件的逻辑代码
        return true; // 返回true表示已经处理了触摸事件,false表示未处理
    }
});

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

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

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

相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
不是株1 天前
零到全栈(网页从哪来,服务器怎么架)
全栈
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas