// PhoneWindow.java
@Override
public boolean superDispatchKeyEvent(KeyEvent event) {
return mDecor.superDispatchKeyEvent(event);
}
// DecorWindow.java
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
// ViewGroup.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
// 丢弃之前无用的事件
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;
// DOWN 事件,或者有目标 target,是否拦截通过onInterceptTouchEvent判断
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 {
// 没有 目标 target 或者 不是初始的 DOWN 事件,都进行拦截
intercepted = true;
}
// 未取消和未拦截
if (!canceled && !intercepted) {
...
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
// 从上往下找到 ViewGroup 中能处理事件的子 View
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
// 所有子 View
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// 找到能正确处理接收事件的 View,跳出循环
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
// 这里就是说当前的child不在之前的列表里
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 如果这个child要拦截事件
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
// 把这个view添加到之前的列表中
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
// 没有找到接收此事件的子View,那就让最后添加进列表的view来处理
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
// TouchTarget这个链表是空的,也就是没有child处理
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
// 对所有的在TouchTarget链表中的child view,一一进行分配
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
return handled;
}
public boolean dispatchTouchEvent(MotionEvent event) {
// 如果设置了 OnTouchListener
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
// 调用 onTouchEvent
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}
// View.java
// 主要根据不同的 Touch 事件作出不同的处理
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
...
case MotionEvent.ACTION_DOWN:
...
case MotionEvent.ACTION_MOVE:
...
}
return true;
}
return false;
ViewGroup 和 View 的伪代码总结
// ViewGroup
fun dispatchTouchEvent(ev: MotionEvent) {
var consume = false
var intercepted = false
intercepted = onTntercepted(ev)
// 没有被拦截,往下传递
if (intercepted == false) {
consume = child.dispatchTouchEvent(ev)
} else {
// 被拦截,交给自己处理,onTouchEvent
consume = onTntercepted(ev)
}
return consume
}
// View
fun dispatchTouchEvent(ev: MotionEvent) {
var result = false
// 如果有 touchListener 的监听
if (mOnTouchListener != null) {
result = mOnTouchListener.onTouch(ev)
}
// 事件没有被消耗,并且有代理,会执行代理的 onTouchEvent.
if (result == false && mTOuchDelegate != null) {
result = mTOuchDelegate.onTouchEvent(ev)
}
// 事件没有被消耗,
if (result == false) {
result = onTouchEvent(ev)
if(MotionEvent.ACTION_UP == action && mClickListener != null) {
mClickListener.onClick(event)
}
}
// 返回事件处理结果
return result
}
TouchEvent
[TOC]
本篇文章从源码角度分析 Android 触摸事件的分发传递机制。
View 的基础
位置参数
top, left, right, bottom,到父 View 的参数
Android 3.0 之后增加的参数:
在 View 平移的过程中,top 和 left 表示原始左上角的位置信息,值不会发生改变;
另外四个参数改变。
View 的滑动
View的事件分发机制
伪代码如下:
Activity
Touch 事件发生,会首先传递给 Activity, 因为以下函数作为整个整个 Touch 事件的入口,开始处理。
ViewGroup
接下来看 window 是怎么进行处理的,这里是 PhoneWindow。
这个部分逻辑比较复杂, 简单来说,先根据 onInterceptTouchEvent 的结果判断当前的 ViewGroup 是否拦截事件。如果拦截,就从维护的 TouchTarget 链表中寻找可以消费事件的 Child。如果不拦截,就遍历所有的子 View, 直到找到消费事件的子 View, 并添加到 TouchTarget 链表中。
中间很很多过程,总结一下, 判断 child 是否为空,对应的方法,都是来到 View中。
View
ViewGroup 和 View 的伪代码总结
总结
上面分别从 Activity,ViewGroup 和 View 三个不同的空间分析的事件传递的大概流程。
其重点在于 ViewGroup 的处理,整个过程类似于 okhttp 中的拦截器,每一层相互独立,结果取决于其他层的结果。