事件分发(ViewGroup)
来源:互联网 发布:ios6怎么下载ios7软件 编辑:程序博客网 时间:2024/05/05 05:17
ViewGroup是继承自View,包含很多View和ViewGroup。上一篇文章说过只要touch一个View,就会执行dispatchTouchEvent方法,ViewGroup也是View,同样会首先执行dispatchTouchEvent方法。
事件的传递是从外层向内层传递的,查看dispatchTouchEvent源码,分析ViewGroup的dispatchTouchEvent:
// 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; }
这段代码可以看出,ViewGroup会在MotionEvent.ACTION_DOWN和mFirstTouchTarget != null的两种情况下判断是否拦截。由后面的代码分析可知当ViewGroup的子元素成功处理事件就不为null。这里的标记位FLAG_DISALLOW_INTERCEPT,是通过requestDisallowInterceptTouchEvent()方法设置的,一般在子View中设置。
MotionEvent.ACTION_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(); }
分情况分析:当ViewGroup决定自己拦截事件的时候 ,mFirstTouchTarget为null,会执行如下代码:
// 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 { ... } }
执行dispatchTransformedTouchEven()方法,如下,child(第三个参数)的为null,调用super.dispatchTouchEvent(event);其实就是View的dispatchTouchEvent(),交由View来处理,事件处理在上一篇View事件分发。
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; // Canceling motions is a special case. We don't need to perform any transformations // or filtering. The important part is the action, not the contents. final int oldAction = event.getAction(); if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); if (child == null) { handled = super.dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } // Calculate the number of pointers to deliver. final int oldPointerIdBits = event.getPointerIdBits(); final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits; // If for some reason we ended up in an inconsistent state where it looks like we // might produce a motion event with no pointers in it, then drop the event. if (newPointerIdBits == 0) { return false; } // If the number of pointers is the same and we don't need to perform any fancy // irreversible transformations, then we can reuse the motion event for this // dispatch as long as we are careful to revert any changes we make. // Otherwise we need to make a copy. final MotionEvent transformedEvent; if (newPointerIdBits == oldPointerIdBits) { if (child == null || child.hasIdentityMatrix()) { if (child == null) { handled = super.dispatchTouchEvent(event); } else { final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; event.offsetLocation(offsetX, offsetY); handled = child.dispatchTouchEvent(event); event.offsetLocation(-offsetX, -offsetY); } return handled; } transformedEvent = MotionEvent.obtain(event); } else { transformedEvent = event.split(newPointerIdBits); } // Perform any necessary transformations and dispatch. if (child == null) { handled = super.dispatchTouchEvent(transformedEvent); } else { final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; transformedEvent.offsetLocation(offsetX, offsetY); if (! child.hasIdentityMatrix()) { transformedEvent.transform(child.getInverseMatrix()); } handled = child.dispatchTouchEvent(transformedEvent); } // Done. transformedEvent.recycle(); return handled; }
ViewGroup不拦截事件的时候,事件会分发给他的子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); // 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)) { 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; } resetCancelNextUpFlag(child); if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. 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(); 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); }
其中:dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign))实际调用的是子View的dispatchTouchEvent方法,如果子元素的dispatchTouchEvent方法返回true,mFirstTouchTarget会被赋值跳出循环。如下所示:
newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true;
赋值最终在addTouchTarget方法:TouchTarget其实是单链表结构
/** * Adds a touch target for specified child to the beginning of the list. * Assumes the target child is not already present. */ private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) { final TouchTarget target = TouchTarget.obtain(child, pointerIdBits); target.next = mFirstTouchTarget; mFirstTouchTarget = target; return target; }
这几行代码完成了对mFirstTouchTarget的赋值并终止了对子元素的遍历。如果子元素的dispatchTouchEvent返回false,ViewGroup就会把事件分给下一个元素(如果还有子元素的话)。
如果子View的dispatchTouchEvent返回false或者没有子元素,ViewGroup会自己处理点击事件(和拦截自己处理一样,第三个参数child为null,调用View的dispatchTouchEvent方法,交由View来处理,事件处理上一篇View事件分发)。
// 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 {...}
- android 事件分发 ViewGroup
- ViewGroup的事件分发
- ViewGroup事件分发机制
- ViewGroup事件分发处理
- ViewGroup事件分发
- ViewGroup事件分发
- ViewGroup事件分发
- ViewGroup的事件分发
- 事件分发(ViewGroup)
- viewGroup事件分发记录
- ViewGroup事件分发
- ViewGroup事件分发
- 事件分发机制---ViewGroup
- ViewGroup事件分发机制
- ViewGroup 事件分发
- ViewGroup的事件分发机制
- Android ViewGroup事件分发机制
- Android ViewGroup事件分发机制
- veloctiy页面bootstrap封装页面radiao设置选中,父标签要选中
- 11.22
- 2014 Multi-University Training Contest 1
- 字符串操作总结
- 自定义Dialog使用场景
- 事件分发(ViewGroup)
- php异常处理基本方法
- Android群英传——第九章packages.xml的作用
- HEVC/H.265理论知识(9)——码率控制
- java 初探(三)
- SQL ------------------- 数据查询基础
- 提升程序员身心健康和工作效率的装备
- 第9章 上机1,2,3
- select warehouse