Android-高级-UI-进阶之路-(二)-深入理解-Android-8-0-View-触摸事件分发机制

public boolean dispatchTouchEvent(MotionEvent ev) {
/**

  • 首先按下的触发的是 ACTION_DOWN 事件
    /
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
    onUserInteraction();
    }
    /
    *
  • 拿到当前 Window 调用 superDispatchTouchEvent 方法
    /
    if (getWindow().superDispatchTouchEvent(ev)) {
    return true;
    }
    /
    *
  • 如果所有的 View 都没有处理,那么最终会执行到 Activity onTouchEvent 方法中。
    */
    return onTouchEvent(ev);
    }

通过上面的代码我们知道首先执行的是 ACTION_DOWN 按下事件执行 onUserInteraction 空方法,然后调用 getWindow() 的 superDispatchTouchEvent 方法,这里的 getWindow 其实就是它的唯一子类 PhoneWindow 我们看它的具体调用实现,代码如下:

//PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {

private DecorView mDecor;
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}

}
复制代码

在 PhoneWindow 的 superDispatchTouchEvent 函数中又交于了 DecorView 来处理,那么 DecorView 是什么呢?

//DecorView.java

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {

DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
super(context);

@Override
public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
}

}

我们看到 DecorView 它其实就是继承的 FrameLayout ,我们知道在 Activity 中我们可以通过 getWindow().getDecorView().findViewById() 拿到对应在 XML 中的 View 对象 , 那么 DecorView 又是什么时候进行实例化呢?还有 PhoneWindow 又是何时进行实例化的呢?因为这些不是咱们今天讲解的主要内容,感兴趣的可以看我之前对 Activity 启动源码分析该篇中有讲过 它们其实都是在 Activity 启动的时候进行各自的实例化。好了,DecorView 实例化就讲到这里。目前事件传递到了 DecorView 这里,我们看它的内部源码实现,代码如下:

// DecorView.java
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
复制代码

我们看到内部又调用了父类 dispatchTouchEvent 方法, 所以最终是交给 ViewGroup 顶级 View 来处理分发了。

  1. 顶级 View 对点击事件的分发过程

在上一小节中我们知道了一个事件的传递流程,这里我们就大致在回顾一下。首先点击事件到达顶级 ViewGroup 之后,会调用自身的 dispatchTouchEvent 方法,之后如果自身的拦截方法 onInterceptTouchEvent 返回 true ,则事件不会继续下发给子类,如果自身设置了 mOnTouchListener 监听,则 onTouch 会被调用,否则 onTouchEvent 会被调用,如果 onTouchEvent 中设置了 mOnClickListener 那么 onClick 会调用。如果 ViewGroup 的 onInterceptTouchEvent 返回 false,则事件会传递到所点击的子 View 中,这时子 View 的 dispatchTouchEvent 会被调用。到此为止,事件已经从顶级 View 传递给了下一层 View ,接下来的传递过程和顶级 ViewGroup 一样,如此循环就完成了整个事件的分发。

在该小节的第一点中我们知道,在 DecorView 中的 superDispatchTouchEvent 方法内部调用了父类的 dispatchTouchEvent 方法,我们看它的实现,代码如下:

//ViewGroup.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

if (actionMasked == MotionEvent.ACTION_DOWN) {
//这里主要是在新事件开始时处理完上一个事件
cancelAndClearTouchTargets(ev);
resetTouchState();
}

/** 检查事件拦截,表示事件是否拦截*/
final boolean intercepted;
/**

  • 1. 判断当前是否是按下
    */
    if (actionMasked == MotionEvent.ACTION_DOWN
    || mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    //2. 子类可以通过 requestDisallowInterceptTouchEvent 方法来设置父类不要拦截
    if (!disallowIntercept) {
    //3
    intercepted = onInterceptTouchEvent(ev);
    //恢复事件防止其改变
    ev.setAction(action);
    } else {
    intercepted = false;
    }
    } else {
    intercepted = true;
    }

    }

从上面代码我们可以看出如果 actionMasked == MotionEvent.ACTION_DOWN 或者 mFirstTouchTarget != null 成立的话会执行注释 2 的判断(mFirstTouchTarget 的意思如果当前事件被子类消费了,就不成立,后面会提高),disallowIntercept 可以在子类中通过调用父类的 requestDisallowInterceptTouchEvent(true) 请求父类不要拦截分发事件,也就是阻止执行注释 3 的拦截子类接收按下的事件,反之执行 onInterceptTouchEvent(ev); 如果返回 true 说明拦截了事件 。

上面介绍了注释 1,2,3 onInterceptTouchEvent 返回 true 的情况,说明拦截了事件,下面我们来讲解 intercepted = false 当前 ViewGroup 不拦截事件的时候,事件会下发给它的子 View 进行处理,下面看子 View 处理的源码,代码如下:

//ViewGroup.java
public boolean dispatchTouchEvent(MotionEvent ev) {

if (!canceled && !intercepted) {

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值