标签:android style blog class code c
对于一个"我们从来不生产代码,我们只是大自然代码的搬运工"的码农来说。对android的触控机制一直是模棱两可的状态,特别是当要求一些自定义的控件和androide的自带控件(比如ViewPager,ListView,ScrollView)高度嵌套在一起使用时。
花了点时间梳理了下,做个笔记。对于一个触控的事件从用户输入到传递到Actigvity到最外层的Viewgroup在到子View,中间过程还可能穿插多个Viewgroup,android在ViewGroup提供了3个方法来控制流程的分发,拦截,和执行。他们按照执行的顺序分别是dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent()。
一.主要方法介绍
打个比喻,所有的事件(可以看做美食),要消化没事,首先先要判断桌前的食物你是否喜欢(dispatchTouchEvent相当选择美食的手)。
dispatchTouchEvent(MotionEvent e):根据入参MotionEvent来决定经过onInterceptTouchEvent和标记变量disallowIntercept(稍候会描述)的值,来判断将事件传递到给自己的onTouchEvent()方法还是传递给子View或者者子ViewGroup来再次进行分发,还是直接自己处理掉(调用自身的onTouchEvent方法)。void requestDisallowInterceptTouchEvent(Boolean disallowIntercept):这个方法的入参一个bool变量,用来表示是否需要调用onInterceptTouchEvent来判断是否拦截.
该标记如果为True,就如它的字面意思一样——不允许调用onInterceptTouchEvent(),结果就是,所有的父类方法都不会进行拦截,而把事件传递给子View. 该方法属于ViewGroup ,并且是个递归方法,也就是说一旦调用后,所有父类的disallowIntercept都会设置成True。即当前View的所有父类View,都不会调用自身的onInterceptTouchEvent()进行拦截。
requestDisallowInterceptTouchEvent方法的代码
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { // We're already in this state, assume our ancestors are too return; } //设置当前View的disallowIntercept if (disallowIntercept) { mGroupFlags |= FLAG_DISALLOW_INTERCEPT; } else { mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } // 递归调用,设置父类的disallowIntercept if (mParent != null) { mParent.requestDisallowInterceptTouchEvent(disallowIntercept); } }
二.总结
下基本的规则是:
1. 如果当前的ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理。注意一点:在整个touch事件过程中,从action_down到action_up,若父ViewGroup的函数onInterceptTouchEvent一旦返回true,消息将不再派发给子view,细分可为两种情况,若是在action_down时onInterceptTouchEvent返回true,不会派发任何消息给子view,并且后面onInterceptTouchEvent函数将不再会被执行,若是action_down时onInterceptTouchEvent返回false ,而后面touch过程中onInterceptTouchEvent==true,父viewGroup会把action_cancel派发给子view,也之后不再派发消息给子view,并且onInterceptTouchEvent函数后面将不再被执行。
很多情况下,在自定义的ViewGroup中继承了onInterceptTouchEvent简单粗暴的返回true,假如自定义的ViewGroup中包含Button,就会发现Button的Onclick方法执行不了。应为要形成一个必须要先要的道一个ACTION_DOWN的事件,onInterceptTouchEvent把Down事件也截取的话,内部的Button自然就不会回掉Onclick了
三.流程图总结
如果仔细查看ViewGroup的onTouchEvent和InterceptTouchEvent这两个类的话,onInterceptTouchEvent的整个过程是没有调用过onTouchEvent的,反之也是,那么onInterceptTouchEvent是如何拦截事件的流程的呢?
答案在dispatchTouchEvent中,dispatchTouchEvent控制了整个流程是被拦截自己消化,还是传递给子View继续消化,是他调用了onInterceptTouchEvent和onTouchEvent进行判断
来看下整个流程用流程图表示如下:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } // Check for interception. final boolean intercepted;//拦截的标记变量 if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { //调用自身的 onInterceptTochEvent,判断是否需要拦截 intercepted = onInterceptTochEvent(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; } //假如没拦截 if (!canceled && !intercepted) { if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { 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(); //遍历所有的子View,并且调用他们的事件分发方法dispatchTouchEvent() 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); //newTouchTarget表示事件传递的View目标,当不为空的时候,直接跳出循环 if (newTouchTarget != null) { newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); //递归调用子View分发事件方法, if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { mLastTouchDownTime = ev.getDownTime(); mLastTouchDownIndex = childIndex; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); //设置分发目标newTouchTarget为当前View newTouchTarget = addTouchTarget(child, idBitsToAssign); //标记子View的分发结果,为True的话,下面的代码是不会调用当前View的onTouch方法的,也就是规则1生成的原因 alreadyDispatchedToNewTouchTarget = true; break; } } } } } 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; //如果刚才alreadyDispatchedToNewTouchTarget设为True的话,就不执行下面的dispatchTransformedTouchEvent //alreadyDispatchedToNewTouchTarget是由子View的onTouch返回值决定的, if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; //执行自身的Touch事件, 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; } } }
public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } //... if (onTouchEvent(event)) {//View的dispatchTouchEvent十分简单,就是调用下自己onTouch事件,并且返回结果。这个结果直接影响以后的事件是否///继续传递 return true; } } //... return false; }
舌尖上的安卓(android触控事件机制学习笔记录),布布扣,bubuko.com
标签:android style blog class code c
原文地址:http://blog.csdn.net/hdxiaoyu2/article/details/25563453