android 事件分发机制 概念理解

android 事件分发机制

参考资料

Android 事件分发机制源码和实例解析

Android View 事件分发机制详解

图解 Android 事件分发机制

图解View的事件分发机制

原理

  1. 分发事件 的起始点:

从 Activity 开始,Activity 源码

Activity 有两个方法 dispatchTouchEvent 和 onTouchEvent

Activity—dispatchTouchEvent

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

    getWindow().superDispathTouchEvent 就是用来分发事件到
    DecorView 中。如果整个 ViewTree 没有消费事件,会调用
    Activity 的 onTouchEvent。

源码中: 
  protected void onUserLeaveHint() {
    }

Activity—onTouchEvent

    public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false;
    }
  1. 分发事件 的终点 也是 Activity 从源码可知

应为 分发的方法是 acticity 自己定义的方法
dispatchTouchEvent,所有最后只有他自己来处理该事件。

  1. 分发事件的过程

主要涉及 View 和 ViewGroup (在 xml 中 设置)

View 只有 onTouchEvent 和 dispatchTouchEvent 两个方法。

ViewGroup 有 onTouchEvent / dispatchTouchEvent 和 onInterceptTouchEvent 三个方法。

图片案例

  1. 注意事项:

View 或 ViewGroup 有两个核心的行为:拦截(intercept) 和 消费(consume)。这两者是相互独立的,拦截不一定消费。是否要拦截看 onIntercepTouchEvent。是否要消费看 onTouchEvent。

  1. 方法理解
dispatchTouchEvent,该方法封装了事件分发的整个过程。是事
件分发的 调度者 和 指挥官 。的核心过程均在该方法中。下面的
onInterceptTouchEvent 和 onTouchEvent
的回调的调用就在该方法体中。是否传递事件到
onInterceptTouchEvent 和 onTouchEvent 由
dispatchTouchEvent 决定。
onInterceptTouchEvent,该方法决定了是否拦截事件。只有
ViewGroup 有该回调。返回 true 表示拦截,返回 false
表示不拦截。自定义 View
的时候,可以重载该方法,通过一些特定的逻辑来决定是否拦截
事件。如果拦截,接下来会调用该 ViewGroup 的 onTouchEvent
来处理事件。
onTouchEvent,该方法处理了事件,并决定是否继续消费后续
事件。该方法调用的前置条件:

该 View 拦截了事件
子 View 都不消费事件
没有子 View


该方法正式处理 MotionEvent。返回 true 表示消费,返回
false 不消费。如果消费,接下来的事件还会传递到该 View 的
dispatchTouchEvent 中;如果不消费,后面的事件不会再传过来。

onTouchListener 的 onTouch 回调,和 onTouchEvent
一样,优先级比 onTouchEvent 高,如果有设置该监听,并且
onTouch 返回 true,就不会再调用 onTouchEvent 了。如果返回
false,事件还是会传递到 onTouchEvent 中。
dispatchTransformedTouchEvent 关键部分

// Perform any necessary transformations and dispatch.
if (child == null) {
    handled = super.dispatchTouchEvent(transformedEvent);
} else {
    final float offsetX = mScrollX - child.mLeft;
    final float offsetY = mScrollY - child.mTop;
    transformedEvent.offsetLocation(offsetX, offsetY);
    if (! child.hasIdentityMatrix()) {
        transformedEvent.transform(child.getInverseMatrix());
    }

    handled = child.dispatchTouchEvent(transformedEvent);
}

也就是 dispatchTransformTouchEvent 完成了分发的最后过程:

a. 传入的 child 不为空,转化坐标为 child 的坐标系,调用
child.dispatchTouchEvent 向 child 分发事件
b. 传入的 child 为空,调用 super.dispatchTouchEvent
分发事件到 onTouchEvent 中

  1. 特殊情况 子 View –requestDisallowInterceptTouchEvent
比较特殊的情况有,子 View 可以使用 requestDisallowInterceptTouchEvent 影响去父 View
的分发,可以决定父 View 是否要调用 onInterceptTouchEvent
。比如,requestDisallowInterceptTouchEvent(true),父 View
就不用调用 onInterceptTouchEvent
来判断拦截,而就是不拦截。

该方法可以用来解决手势冲突。比如子 View
先消费了事件,但是后面父 View
也满足了手势触发的条件而拦截事件,导致子 View
手势执行一半后无法继续响应。可以使用
requestDisallowInterceptTouchEvent(true),这样后面的事
件,父 View 不会走 onInterceptTouchEvent
回调来判断是否要拦截事件,而是直接把事件继续传下来。

猜你喜欢

转载自blog.csdn.net/hyl411136528/article/details/54091889
今日推荐