Android view 的事件分发机制是Android应用开发中绕不开的问题,如何解决各种嵌套滑动问题,最主要的是了解view 对事件的分发机制,在此记录下我对dispatchTouchEvent 源码的理解
···
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev,1);
}
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
//如果该事件以可访问性为重点的视图为目标,那就开始
// 正常事件调度。 也许后代将处理点击。
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false); }
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {//安全性过滤
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;//动作代码中属于动作本身的部分的位掩码 位操作
// Handle an initial down.如果点击事件问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.
//
/*
开始新的触摸手势时,放弃所有先前的状态。
框架可能已放弃上一个手势的up或cancel事件
由于应用切换,ANR或其他一些状态更改。*/
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.检查拦截
final boolean intercepted;//是否拦截
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget !=null) {//事件为down事件,或者mFirstTouchTarget不等了空(这个时候 mFirstTouchTarget为空)
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;//判断当前子view 是否允许 当前viewgroup 拦截事件
if (!disallowIntercept) {//允许拦截
intercepted = onInterceptTouchEvent(ev);//调用自己的onInterceptTouchEvent 事件
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;
}
// If intercepted, start normal event dispatch. Also if there is already
// a view that is handling the gesture, do normal event dispatch.
//如果被拦截,请开始正常事件分发。 另外如果已经有
// 一个正在处理手势的视图,请进行正常的事件分配
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation. 检查取消。
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
//如有必要,更新触摸目标列表以使指针向下
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget =null; //分发的子view
boolean alreadyDispatchedToNewTouchTarget = false;//是否已经分发给新的view
if (!canceled && !intercepted) {//如果没有取消并且当前没有拦截事件
// If the event is targeting accessibility focus we give it to the
// view that has accessibility focus and if it does not handle it
// we clear the flag and dispatch the event to all children as usual.
// We are looking up the accessibility focused host to avoid keeping
// state since these events are very rare.
//如果该事件以可访问性为目标,则将其分配给
//具有可访问性焦点的视图,如果它不处理它
//我们清除标志并将事件照常分派给所有孩子。
//我们正在查找以可访问性为重点的主机,以避免保持
//说明,因为这些事件非常少见。(大概意思:如果当前viewgroup 没有拦截,)
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() :null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)//多点触控的down
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down 始终为0 也就是取第一个事件作为down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
// 清除此指针ID的较早触摸目标,以防它们
//已不同步。
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {//newTouchTarget 这个时候肯定为空 并且有子view
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.
//找到一个可以接收该事件的孩子。
//从前到后扫描孩子。
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {//从前往后遍历子view
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);//或者孩子正确的指针(有可能是自定义view)
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
//如果有一个具有可访问性焦点的视图,我们希望它
//首先获取事件,如果未处理,我们将执行
//正常分发。 我们可以进行两次迭代,但这是
//在指定的时间范围内更安全。
if (childWithAccessibilityFocus != null) {//如果可点击
if (childWithAccessibilityFocus != child) {//未处理
continue;
}
childWithAccessibilityFocus =null;
i = childrenCount -1;//遍历下一个孩子
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child,null)) {//如果不可点击或者点击事件不在这个view上
ev.setTargetAccessibilityFocus(false);
continue;
}
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;
}
//如果没找到可以处理事件的子view
//重置取消下一个向上标志。
// 如果该标志先前已设置,则返回true。
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {//事件被处理了
//dispatchTransformedTouchEvent
// Child wants to receive touch within its bounds.
//子view 希望在其范围内获得接触。
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
//childIndex指向预排序列表,找到原始索引
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
}else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
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();
}
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.
//没有触摸目标,因此请将其视为普通视图。
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 predecessor =null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled =true;//返回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;
}
}
// Update list of touch targets for pointer up or cancel, if needed.
//如果需要,更新触摸目标列表以向上或取消指针。
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
}else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}
if (!handled && mInputEventConsistencyVerifier != null) {//如果事件没被处理
mInputEventConsistencyVerifier.onUnhandledEvent(ev,1); //未处理事件
}
return handled;
}
···
网友评论