Android事件传递
来源:互联网 发布:淘宝买家退货率高 编辑:程序博客网 时间:2024/06/05 07:37
事件分发:dispatchTouchEvent
事件拦截:onInterceptTouchEvent
事件消费:onTouchEvent
public static final int ACTION_DOWN = 0;
public static final int ACTION_UP = 1;
public static final int ACTION_MOVE = 2;
MainActivity:
–>boolean dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction();//这里是一个空方法 } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev);}
PhoneWindow:
-->boolean superDispatchTouchEvent()
DecorView:这里DecorView就是一个FrameLayout,所以他的父类就是ViewGroup
-->boolean superDispatchTouchEvent()
ViewGroup:
–>boolean dispatchTouchEvent()
@Overridepublic 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); //清除FLAG_DISALLOW_INTERCEPT设置并且mFirstTouchTarget设置为null resetTouchState(); } // Check for interception. final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { //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 { //当事件不为ACTION_DOWN且事件由ViewGroup自己处理时,没有必要判断是否拦截 // 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; boolean alreadyDispatchedToNewTouchTarget = false; //没有取消事件也没有拦截 if (!canceled && !intercepted) { // If the event is targeting accessiiblity 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. View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; 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; //没有控件消费了事件且子控件数量不为0 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. //将list排序,查找的时候先查找在屏幕上层的控件 final ArrayList<View> preorderedList = buildTouchDispatchChildList(); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; //倒着遍历,先从屏幕最上方的控件开始 for (int i = childrenCount - 1; i >= 0; i--) { 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; } //child没有执行动画且点击的范围在child的范围内 //以上两个条件有一个不满足,就continue去看下一个View 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); } 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. //这里第三个参数为null 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; } } ...... return handled;}
在里面调用了dispatchTransformedTouchEvent()
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; ...... // 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;}
调用了父类的或者子类重写了的dispatchTouchEvent
View:
–>boolean dispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent event) { ...... //窗口没有被遮盖 if (onFilterTouchEventForSecurity(event)) { if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { result = true; } //noinspection SimplifiableIfStatement //如果设置了onTouch事件,且返回true,那么就不在执行onTouchEvent了 ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } //如果onTouchListener返回true,就不会走onTouchEvent了 if (!result && onTouchEvent(event)) { result = true; } } ...... return result;}
–>boolean onTouchEvent()
public boolean onTouchEvent(MotionEvent event) { ...... if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { switch (action) { case MotionEvent.ACTION_UP: ...... // Only perform take click actions if we were in the pressed state if (!focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state // of the view update before click actions start. if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { //这里回调了我们的onClick事件 performClick(); } } } ...... return false;}
–>boolean performClick()
public boolean performClick() { final boolean result; final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result = true; } else { result = false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); return result;}
总结一下
dispatchTouchEvent是事件分发
如果返回true,那么整个事件就停止了,不会再走其他的方法;如果返回false,就会直接回调父控件的onTouchEvent事件;如果返回了super.dispatchTouchEvent,就将事件向下传递
onInterceptTouchEvent是事件拦截
View没有拦截方法,ViewGroup才有
在viewGroup的dispatchTouchEvent返回super的时候会执行如果返回true,表示我要拦截事件,事件将不再向下传递,而是直接走自己的onTouchEvent方法如果返回false或者super,则会向下传递
onTouchEvent是事件消费
如果返回true,表示我要消费,事件到此中断如果返回false或者super,都会向上传递,交给子控件消费
如果子控件不想被父容器拦截,可以调用requestDisallowInterceptTouchEvent(true);
我们都知道如果给一个控件注册了onTouch事件,每次点击都会出发Down,Move,Up等事件.这里要注意一点,如果在Down事件里返回了false,后面的一系列事件都不会执行了。意思就是,在dispatchTouchEvent事件分发的时候,只有前一个action返回了true,才会执行下一个action.
onTouch和onTouchEvent的区别
从源码中可以看出,onTouch和onTouchEvent都是在view的dispatchTouchEvent中回调的,onTouch优于onTouchEvent,如果设置了onTouchListener,并且在onTouch中返回了true,就不会在走onTouchEvent了,这里要注意一下,onTouch执行的两个条件,一个是设置了onTouchListener,另外一个是控件必须是enable的,否则不会执行。比如ImageView,可以设置android:clickable="true",使得可以点击
- android 事件传递机制
- android 事件传递机制
- android触摸事件传递
- android事件传递机制
- android 事件传递
- Android触摸事件传递
- Android事件传递机制
- Android 事件传递机制
- android 事件的传递
- Android事件传递机制
- Android中的事件传递
- Android事件传递机制
- Android事件传递机制
- Android事件传递机制
- Android事件传递机制
- Android事件传递机制
- Android事件传递机制
- android touch事件传递
- Golang请求网页返回数据乱码处理一例
- git命令之git tag 给当前分支打标签
- unity的scriptableObject的使用
- tabbar视图控制器之间的切换
- MFC禁止对话框的拖动
- Android事件传递
- vim中Ctrl+t和Ctrl+o快捷键的区别
- mysql日志文件恢复数据
- 通过BlockingQueue实现生产者和消费者问题
- 一、GIT创建版本库基本命令
- Linux Driver教程PPT
- SAS统计分析学习笔记(八)——T检验和非参数比较
- C++ map基本操作实例
- UIWebView 追加UA信息