从一个聊天信息引发的思考之Android事件分发机制
转载请声明:http://www.cnblogs.com/courtier/p/4295235.html
- 起源:
我在某一天看到了下面的一条信息(如下图),我想了下(当然不是这个人问的问题)“为什么Activity能够与界面交互和为什么它们
的事件能够传递起来?”我带着这些疑问,自己上网查阅了一些资料与信息,从而得出以下的原理。
-
Activity Window View 的关系:
我的归纳:众所周知,Activity并非是真正的显示对象。Activity类有个成员变量叫做:mWindow的类型为Window,而这个才是
我们显示窗体的类型-Phone Window(PolicyManager.makeNewWindow()创建)。PhoneWindow类有两个重要的成员变量
mDecor和mContentParent 。其中,mDecor(内部类)是主类,而mContentParent我们加入的View就是放在这里。如下图:
而我们主类(窗口类)如何来改变是由:ViewRoot来决定的。既然,主类(窗口类)依赖于ViewRoot,那么,我们的ViewRoot
就持有Phone Window的实例(即mDecor)。ViewRoot对象都有一个类型为WindowManager.LayoutParams的成员变量
mWindowAttributes,它指向了一个ViewGroup.LayoutParams对象,用来描述与该ViewRoot对象对应的一个Activity组件
的UI布局信息。那么,我们ViewRoot何时知道改变呢?主要取决于:WindowManagerService(负责事件处理)。
那么,长话短说看个图即可:(IWindowSession是ViewRoot的变量)
(来源于:http://blog.csdn.net/wangjinyu501/article/details/9008271?utm_source=tuicool)
-
事件机制:(有了以上的东西可以明白消息是怎么样传到Activity)
(wms->viewroot->phonewindow.DecorView->Activity->phonewindow->phonewindow.DecorView->ViewGroup)
-
从ViewGroup开始去理解源代码:
public class FrameLayout extends ViewGroup { //...... // Check for interception. final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { //disallowIntercept:表明如果子控件请求父控件不要拦截事件。 //调用requestDisallowInterceptTouchEvent 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; } //.. //如果,拦截了就是MotionEvent.ACTION_CANCEL final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; //不拦截就处理ActionDown if (!canceled && !intercepted) { if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { final int actionIndex = ev.getActionIndex(); // always 0 for 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. removePointersFromTouchTargets(idBitsToAssign); final int childrenCount = mChildrenCount; 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. final View[] children = mChildren; final boolean customOrder = isChildrenDrawingOrderEnabled(); for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = children[childIndex]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { 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(); mLastTouchDownIndex = childIndex; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } } } 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. //上面执行的方法不会执行的条件就是: //1:你的动作不是ActionDown //2:已经启动拦截事件 if (mFirstTouchTarget == null) { //mFirstTouchTarget==null:只有两种情况: //第一种:上面有拦截 //第二种:OnTouch事件没有被消费(即子类没有能力消费父类的)也就是说处理了ACTION_DOWN事件后,发现没办法处理才返回 // No touch targets so treat this as an ordinary view. //dispatchTransformedTouchEvent:重新调用父类类 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; } 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; } } //....ViewGrouo结束 }
- View开始去理解源代码:(ViewGroup下面的各种View)
public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; //li.mOnTouchListener.onTouch:就是我们经常调用的OnTouchListener,返回值: //True:不会执行我们的(onTouchEvent就是我们的Onclicklistener方法了 //False:就会执行Onclicklistener if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { return true; } if (onTouchEvent(event)) { return true; } } if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } return false; }
- 总结:
1:关于拦截事件:
问题1:为什么要有拦截事件(思考如下):我觉得是因为两个事件(父子View)有冲突,系统执行哪个存在疑问。拦截掉子类的。
拦截事件有几个重要点:1:被拦截后子控件收到的都是ACTION_CANCEL。2:父类一次拦截终身拦截。
2:OnTouchListener 和 OnClickListener:
问题2:为什么执行完OnTouchListener 返回真后就不能执行下面的了?我觉得是因为,两个事件具有重合性。Touch事件:已经
包含了有(Click的性质方面)。
3:参考很多高手的文章(十几篇吧)在此表示感谢,让我学习完豁然开朗,有写错的请留言。谢谢。新年快乐!
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。