分析UI中的TouchEvent传递

touchEvent也是一种Input,所以要从InputManager说起,不管触摸屏幕还是通过cmd命令adb input都是走的InputManger,InputManager会把事件分发对当前获得焦点的ViewRootImpl。即用户进程中。递交到Activity的处理是这样的。

Activity.java

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

PhoneWindow.java

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

//mDecor view inner
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}

mDecor就是应用的根View,所以点击的事件就从这里传递下去。可能我们比较关心的是在用户进程的传递,所以接下来的分析都集中在APP的UI进程中。这里很难描述清楚全部细节,所以这里我提出几个问题,带着问题去找答案。

1,点击的事件的传递是否是单向的?
2,我们执行拖拽的时候,讲道理应该是找不到最根部的View。那么是否有规则,使得handle了ACTION_DOWN事件的View可以继续handle后续的TouchEvent?
3,传递过程中,谁来决定事件传递的停止?

重点要分析的代码应该是集中在ViewGroup.java && View.java中,典型的View结构是这样的

    ViewGroup
    |       \
ViewGroup ViewGroup
    |           |
ViewGroup      ...
    |    \
   View    View

这里只是简单的分析单点触控。先从ViewGroup说起,以每次ACTION_DOWN为界,ViewGroup会找能handle的ViewGroup

final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
    if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
        ...
    }
}

所以第一个问题,dispatchTransformedTouchEvent 会把事件dispatchTouchEvent进子View,如果存在的话。从代码中确实可以看到是单向的,自根向下的,从最后一个chrildview到第一个childview,所以越晚add的view,越先被dispatch。

第二个问题在代码中找到一个成员变量mFirstTouchTarget 缓存了上一次ACTION_DOWN获得的对象,TouchTarget是个链表。所以多指触控的时候,是同时缓存了多个对象。所以是针对ACTION_DOWN这一类的按下事件做了特殊处理。

第三个问题,dispatchTouchEvent有返回值,如果返回值是True,那么事件已经被handle。handle之后就返回了,所以事件的传递就中止了。在dispatchTouchEvent中,使用了一个onTouchEvent作为其中一个判断依据,即dispatchTouchEvent会依赖onTouchEvent的处理结果。
需要指出的是ViewGroup中有一个onInterceptTouchEvent的方法,在分发过程中,ViewGroup可以主动停止事件的分发。

// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
        || mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action); // restore action in case it was changed
    } else {
        intercepted = false;
    }
} else {
    // There are no touch targets and this action is not an initial down
    // so this view group continues to intercept touches.
    intercepted = true;
}

两个dispatchTouchEvent的函数接近两百行代码,不带着问题看容易迷失…

代码主要在

./framework/base/core/java/android/view/ViewGroup.java
./framework/base/core/java/android/view/View.java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值