背景知识:

  1. 触摸屏可以有多个触控点
  2. android中管理触控点通过一个数组来管理,涉及到index和id两个变量,

index表示在数组中的下标,id表示这个触控点(pointer)的id,pointer对应的index子不同的MotionEvent中是可以变化的,

但是它的id是不会变的。

在不同的控件类型上,touch事件的传递方式会不一样。

  • 普通View的touch事件处理过程:

1.view消耗touch事件的地方要有两个一个是OnTouchListener,另一个则是onTouchEvent,OnTouchListener 比onTouchEvent优先级高

2.只要view是CLICKABLE或则LONG_CLICKABLE 或者CONTEXT_CLICKABLE ,无论是否 disable,它在onTouchEvent阶段默认都会消耗这个事件

  • ViewGroup的touch事件处理思路:

     ViewGroup的touch事件下面几个方面是要考虑的

     1.一般的事件序列的过程是ACTION_DOWN,ACTION_MOVE.................,最后ACTION_UP

2.确定是否是ACTION_DOWN事件

3.确定是否intercept

4.确定是否要cancel

源代码代码中mFirstTouchTarget 变量很关键,它保存着处理当前事件序列的view,在ACTION_DOWN的时候确定将新的view加入到这个列表中来,

当在ACTION_MOVE过程中确定当前ViewGroup需要intercept了,那么应该清理掉mFirstTouchTarget中所有的view,因为这个事件序列之后的的event都不会派发给这些view了。

总之,ViewGroup处理touch时间的需要遵守下面几条

1.事件是从父view传递到子view的,如果子view不处理,那么父view再来处理

2.如果ViewGroup的某个子view处理了ACTION_DOWN或则 ACTION_POINTER_DOWN(View.OnTouchEvent默认是返回ture的,也就是处理该事件序列的), 那么这个事件序列后续的事件都传给这个view处理,除非View.dispatchTouchEvent返回false或者 当前ViewGroup确定需要intercept了。

3.ViewGroup的onInterceptTouchEvent 这个函数有机会在将事件传递给子view之前获得调用的机会,以确定是否需要intercept

下面是对ViewGroup.dispatchTouchEvent代码一段分析

public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
} // If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
//ev 是否设置 FLAG_TARGET_ACCESSIBILITY_FOCUS这个
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
} 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.
/*
* ACTION_DOWN重新设置状态,给mFirstTouchTarget中的target发生cancel事件,
* 清空mFirstTouchTarget列表
* 清空mGroupFlags的FLAG_DISALLOW_INTERCEPT标志
* mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
*/
cancelAndClearTouchTargets(ev);
resetTouchState();
} // Check for interception.
final boolean intercepted;
/*在 ACTION_DOWN 和 mFirstTouchTarget不为空的时候就会去intercept
* 在ACTION_MOVE事件的时候,虽然有子view处理事件序列了,
* 但是viewgroup还是有机会插手事件序列
* */
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
//是否不允许intercept
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//允许intercept
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;
} // 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) {
//取消FLAG_TARGET_ACCESSIBILITY_FOCUS
ev.setTargetAccessibilityFocus(false);
} // Check for cancelation.
//取消PFLAG_CANCEL_NEXT_UP_EVENT标记,返回之前是否已经 PFLAG_CANCEL_NEXT_UP_EVENT
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; //canceled==false && interepted==false
if (!canceled && !intercepted) {
//没有intercepted也没有cancel掉,转发到子view中去 // 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; //ACTION_POINTER_DOWN,会有多个触控点
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 //根据索引查找到在ev中id
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.
//根据Z-order得到拍好序的children view list
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
//child 在绘制次序中的次序
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(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;
} //child是否已经在mFirstTouchTarget列表中,多点触控时,几个点都在一个view上
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)) {
//将事件投递到子view成功
// 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();
//加入到mFirstTouchTarget队头
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();
} //如果都没有没找到,那么用mFirstTouchTarget队列中尾巴上的那个target来处理
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.
//如果是ACTION_MOVE事件,上面的这个if 是没有不会执行的,直接到这个if了
if (mFirstTouchTarget == null) {
/*这种状况会是 ACTION_MOVE事件,并且这个时候是 intercepted==true的状况
*这个会转发到View.dispatchTouchEvent() ,这View类中会调用OnTouchListener,和onTouchEvent
*/
// 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;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
//新加入的 target,跳过
handled = true;
} else {
/*
* 如果之前设置calcel标记或者intercepted了
* 如果当前viewgroup 确定intercepted了,
* 那么给mFirstTouchTarget中的所有view发送ACTION_MOVE事件,
* 并且移除mFirstTouchTarget中所有的target
*
* 如果没有intercepted那么照原事件发送
*
*/
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
//如果intecepted了,那么会给之前的target发送一个cancel事件
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
} //移除这个target
if (cancelChild) {
if (predecessor == null) {
//第一个节点
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
//这个continue很关键,它会保持predecessor==null的状况,最后会出现mFirstTouchTarget==null的状况
continue;
}
}
predecessor = target;
target = next;
}
} // Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
//取消或者所有手指都起开
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
//值起开一个手指
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
} if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}

  

android 触摸事件分析的更多相关文章

  1. iOS 和 Android 触摸事件传递

    先看文章,写得很好 ios 触摸事件传递 http://www.cnblogs.com/Quains/p/3369132.html 另外一篇 http://blog.csdn.net/yongyinm ...

  2. 对于android触摸事件模型的一些理解

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  3. 初识Android触摸事件传递机制

    前言 今天总结的一个知识点是Andorid中View事件传递机制,也是核心知识点,相信很多开发者在面对这个问题时候会觉得困惑,另外,View的另外一个难题滑动冲突,比如在ScrollView中嵌套Li ...

  4. Android触摸事件的应用

    前言 上一篇讲了Android触摸事件的传递机制,具体可以看这里 初识Android触摸事件传递机制.既然知道Android中触摸事件的传递分发,那么它能解决什么样的问题,在我们实际开发中如何应用,这 ...

  5. Android触摸事件传递机制

    简单梳理一下Android触摸事件传递机制的知识点. 一.View与ViewGroup的关系 View和ViewGroup二者的继承关系如下图所示: View是Android中最基本的一种UI组件,它 ...

  6. 一个demo让你彻底理解Android触摸事件的并发

    注:本文涉及的demo的地址:https://github.com/absfree/TouchDispatch 1. 触摸动作及事件序列 (1)触摸事件的动作 触摸动作一共有三种:ACTION_DOW ...

  7. 【知识梳理1】Android触摸事件机制

    前言 随着科学技术的发展,智能手机早已成为我们当代人身边不可缺少的"伙伴"之中的一个,堪比对象女友.每天我们对着手机反复的做着点击.滑动操作,而手机则随着我们的操作给我们展示她的精 ...

  8. Android触摸事件流程剖析

    Android中的触摸事件流程就是指MotionEvent如何传递,主要包括两个阶段: onInterceptTouchEvent触摸事件拦截方法传递,从外到里传递 onTouchEvent触摸事件处 ...

  9. 图解Android触摸事件分发

    Android中触摸事件传递过程中最重要的是dispatchTouchEvent().onInterceptTouchEvent()和onTouchEvent()方法. View和Activity有d ...

随机推荐

  1. JAVASE02-Unit010: 多线程基础 、 TCP通信

    多线程基础 . TCP通信 * 当一个方法被synchronized修饰后,那么 * 该方法称为同步方法,即:多个线程不能同时 * 进入到方法内部执行. package day10; /** * 当多 ...

  2. Android中如何查看内存

    文章参照自:http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-a ...

  3. nexus

    下载地址:http://pan.baidu.com/s/1nvwIoa9   (Jfrog/Nexus) maven 仓库: http://mvnrepository.com/ 用户名密码分别是:ad ...

  4. linux下设置固定IP

    编辑网卡配置文件 vi /etc/sysconfig/network-script/ifcfg-eth0 进入编辑模式 按i键进行编辑修改 DEVICE=eth0 #物理设备名 IPADDR=192. ...

  5. AngularJS 2.0

    https://angular.io/docs/ts/latest/guide/learning-angular.html QuickStart: git clone https://github.c ...

  6. iOS代码签名理解

    前言 做了几年iOS app coder了,对于证书的生成.使用流程烂熟于心,然而对于这套机制的原理却一直不甚理解.近来由于工作需要仔细研究了一下,特将自己的学习经验记录于此,以供大家学习指正. 问题 ...

  7. Css深入理解之浮动_慕课网课程笔记

    前言 这篇是在慕课网上跟着张鑫旭重走CSS之路的第三篇学习笔记了,主要是学习float属性,闲话少说,下面进入正文. float的历史 要想了解一个东西,我们还是需要从本质去了解它,那么我们就需要问一 ...

  8. Leetcode: Design Snake Game

    Design a Snake game that is played on a device with screen size = width x height. Play the game onli ...

  9. WORD学习之插入分页符

    我们在用Word编辑文档时,不可避免的需要对文档进行页面布局,才能使得文档看起来更加美观.有时候会使用到分页符,下面就简单介绍一下分页符 分页符 主要作用: 1.若要把两段分开在两页显示时,把光标定位 ...

  10. Ubuntu下安装Docker

    1. 安装前先检查系统对docker的支持,尽可能安装高版本的系统,比如Ubuntu14.04等,安装前可以先检查系统信息. Docker需要64位机器,需要运行在3.8以上的内核上,需要操作系统支持 ...