美文网首页
viewGroup dispatchTouchEvent 源码记

viewGroup dispatchTouchEvent 源码记

作者: BIGLEFT_393d | 来源:发表于2020-07-12 10:46 被阅读0次

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;  

    }  

···

流程图:

相关文章

网友评论

      本文标题:viewGroup dispatchTouchEvent 源码记

      本文链接:https://www.haomeiwen.com/subject/rvdgcktx.html