zoukankan      html  css  js  c++  java
  • Android事件分发机制

    Activity 中的事件分发机制
    Activity 中包含两个事件分发与处理的方法:

    //事件分发
    public boolean dispatchTouchEvent(MotionEvent ev);
    //事件消费
    public boolean onTouchEvent(MotionEvent event);
    1
    2
    3
    4
    我们知道,事件最先是传到 Activity 中,然后在其内部分发之后再传递给ViewGroup 。也就是说 ViewGroup 中的事件上游其实是 Activity,但实际上,ViewGroup 事件的上游不仅仅可以是 Activity,还可以是 Dialog,更进一步的说:ViewGroup 事件的上游是一个 Window。

    我这里只介绍 Activity 中的事件分发机制,其它的有兴趣可以自行阅读源码。
    首先,Activity 中的事件同样起源于 dispatchTouchEvent 方法:

    public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
    return true;
    }
    return onTouchEvent(ev);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    第一步是判断事件如果是点击事件,则调用 onUserInteraction 方法,该方法的方法体为空,我们可以重写这个方法,用于处理一些交互问题。

    第二步是调用了 Window 的 superDispatchTouchEvent 方法,如果返回 true,则表示此事件已被消费,结束此次分发流程,false 则继续调用该 Activity 的 onTouchEvent 方法处理该事件。

    再来说 superDispatchTouchEvent 这个方法是什么,我们知道 Window 是个抽象类,具体实现为 PhoneWindow,我们看看 PhoneWindow 中的这个方法的实现:

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
    }
    1
    2
    3
    4
    其中的 MDecor 指的是 DecorView, DecorView 是整个视图树的根节点视图,也就是说 Activity 的事件被传到了视图树的根节点,我们来看看 DecorView 中的 superDispatchTouchEvent 方法:

    public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
    }
    1
    2
    3
    只是调用了父类的 dispatchTouchEvent 方法,它的父类是 FrameLayout,此时的事件已经传递到了视图树,开始通过 ViewGroup 的逻辑进行分发,事已至此,一切都很明确了,Activity 已经将事件传递给了 ViewGroup。

    总结一下,事件进入 Activity#dispatchTouchEvent 开始分发,首先会将该事件传递给该页面的 ViewGroup,ViewGroup 如果消费了该事件,则分发结束,未消费则继续调用 Activity#onTouchEvent 方法处理事件,流程如下图:

    现在再来看 ViewGroup 的事件分发机制。

    ViewGroup 中的事件分发机制
    ViewGroup 中包含三个事件分发相关的方法:

    //事件分发
    public boolean dispatchTouchEvent(MotionEvent ev);
    //事件拦截
    public boolean onInterceptTouchEvent(MotionEvent ev);
    //事件消费
    public boolean onTouchEvent(MotionEvent event);
    1
    2
    3
    4
    5
    6
    事件首先会进入 dispatchTouchEvent 方法开始分发,该方法通过调用 onInterceptTouchEvent 方法判断是否需要拦截事件。
    如果需要拦截(方法返回 true),则表示当前 ViewGroup 希望处理该事件,或者不希望子 View 处理该事件,此时将直接调用 onTouchEvent 方法。
    如果不需要拦截,则将该事件传递给子 View 的 onTouchEvent 方法,或者子 ViewGroup 的 dispatchTouchEvent 方法。

    现在再来说 onTouchEvent 方法, 该方法用于消费事件,返回值表示是否已消费,在两种情况下会被调用:onInterceptTouchEvent 方法确认需要拦截该事件以及子 View/ViewGroup 未消费该事件。
    如果该方法返回 true,则表示事件已消费,此次事件分发就成结束;
    如果返回 false,则表示未消费该事件,会继续调用父 ViewGroup 或 Activity 的 onTouchEvent 方法。

    对于 ViewGroup 来说,事件拦截与事件消费是两个概念。
    事件拦截对应 onInterceptTouchEvent 方法,返回 true 表示拦截该事件。
    事件消费对应 onTouchEvent 方法,返回 true 表示消费此事件。
    拦截事件表示该事件将不会向下传递,也就是说不会传递给子 View。
    消费事件表示该事件将不会向上传递,也就是说不会传递给父 ViewGroup 的 onTouchEvent 方法。
    我们可以拦截但不消费某个事件,同样,可以也消费但不拦截某个事件。

    其分发流程如下图所示:


    View 中的事件分发机制
    View 中包含如下两个用于处理事件分发相关的方法:

    //事件分发
    public boolean dispatchTouchEvent(MotionEvent event);
    //事件消费
    public boolean onTouchEvent(MotionEvent event);
    1
    2
    3
    4
    ViewGroup 会将事件传递给 dispatchTouchEvent 方法,由于不是 ViewGroup,不需要考虑向下传递事件的逻辑,所以 View 中的事件处理流程很简单。
    抛开 dispatchTouchEvent 中其它的代码来看,其中主要是用来调用 onTouchEvent 方法,并返回 onTouchEvent 的返回值,流程如下:


    上述便是 Android 事件机制的完整流程,实际上事件分发机制还有很多要处理的东西,我这里只是介绍了一些重点及关键点。
    下面我来用一张图总结一下:


    其他
    另外还有一些需要注意点的点。
    除了上面说的这几个事件分发相关的方法外,我们经常会调用 View#setOnTouchListener 来处理事件,例如下面这样:

    view.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
    return false;
    }
    });
    1
    2
    3
    4
    5
    6
    当我们调用了这个方法后,dispatchTouchEvent 会优先调用 OnTouchListener#onTouch 方法判断是否消费该事件,不消费才会继续调用 onTouchEvent 方法。

    还有一点,我们设置 OnTouchListener 时 IDE 会提示一个警告,或者我们直接重写 View#OnTouchEvent 方法时同样会提示这个警告:

    Custom view has setOnTouchListener called on it but does not override performClick
    1
    提示我们没有重写 performClick 方法,那我重写之后还是有警告,不过这次变成了这样:

    onTouchEvent should call performClick when a click detected
    1
    意思就是由于我们重写了 onTouchEvent 方法,可能会导致设置 setOnClickListener 方法失效,我们需要在合适的时候去调用 performClick 方法,我们可以这样:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
    Log.i(TAG, "ViewGroup->onTouchEvent:" + event.toString());
    if(event.getAction() == MotionEvent.ACTION_DOWN){
    return performClick();
    }
    return super.onTouchEvent(event);
    }

    @Override
    public boolean performClick(http://www.my516.com) {
    return super.performClick();
    }
    --------------------- 

  • 相关阅读:
    iot 表索引dump《2》
    heap表和iot表排序规则不同
    Cannot complete the install because one or more required items could not be found.
    iot表输出按主键列排序,heap表不是
    iot 表主键存放所有数据,且按数据插入顺序排序
    iot表和heap表排序规则不同
    org.eclipse.graphiti.ui.editor.DiagramEditorInput.
    Oracle 排序规则
    perl 异步超时 打印错误
    14.6.3 Grouping DML Operations with Transactions 组DML操作
  • 原文地址:https://www.cnblogs.com/ly570/p/11021230.html
Copyright © 2011-2022 走看看