标签: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