转载请声明: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:参考很多高手的文章(十几篇吧)在此表示感谢,让我学习完豁然开朗,有写错的请留言。谢谢。新年快乐!

从一个聊天信息引发的思考之Android事件分发机制的更多相关文章

  1. Android事件分发机制(下)

    这篇文章继续讨论Android事件分发机制,首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子 ...

  2. 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...

  3. Android事件分发机制四:学了事件分发有什么用?

    " 学了事件分发,影响我CV大法吗?" " 影响我陪女朋友的时间" " ..... " 前言 Android事件分发机制已经来到第四篇了,在 ...

  4. Android事件分发机制五:面试官你坐啊

    前言 很高兴遇见你~ 事件分发系列文章已经到最后一篇了,先来回顾一下前面四篇,也当个目录: Android事件分发机制一:事件是如何到达activity的? : 从window机制出发分析了事件分发的 ...

  5. Android事件分发机制(上)

    Android事件分发机制这个问题不止一个人问过我,每次我的回答都显得模拟两可,是因为自己一直对这个没有很好的理解,趁现在比较闲对这个做一点总结 举个例子: 你当前有一个非常简单的项目,只有一个Act ...

  6. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  7. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

  8. 【自己定义控件】android事件分发机制

    自己定义Viewgrou中我们或许会常常碰到这种情况,2个子控件的事件冲突导致滑动没实用了.滑动反应非常慢,点击没用了,要划非常多次才移动一点点等等.或许我们第一反应就是百度,google去搜索下答案 ...

  9. Android 事件分发机制具体解释

    很多其它内容请參照我的个人网站: http://stackvoid.com/ 网上非常多关于Android事件分发机制的解释,大多数描写叙述的都不够清晰,没有吧来龙去脉搞清晰,本文将带你从Touch事 ...

随机推荐

  1. 超好用文件对比工具 – Beyond Compare

    超好用文件对比工具 – Beyond Compare,开发中文件.目录对比神器,有了它,再也不用为找不到修改的内容而发愁了. 具备的丰富实用功能: 并列比较文件夹.FTP 网站或 Zip 文件: 为以 ...

  2. Android(java)学习笔记258:JNI之hello.c(c代码功能实现)指针语法解析

    1. 接下来我们细讲分析一下前面一讲中,c功能实现的代码: (1)hello.c : #include <jni.h> char* getHello() { //////// return ...

  3. LSI SAS 2208 配置操作

    配置LSISAS2208 介绍LSISAS2208扣卡的配置方法. 2.1 登录CU界面 介绍登录LSISAS2208的CU配置界面的方法,以及CU界面的主要功能. 2.2 创建RAID 介绍创建RA ...

  4. mockServer学习

    mockServer学习 很喜欢mockserver官方主页的背景颜色和格式 官方主页如下: http://www.mock-server.com/

  5. java多线程心得

    多并发的时候,在什么情况下必须加锁?如果不加锁会产生什么样的后果. 加锁的场景跟java的new thread和Runnable的关系是什么? 看看java的concurrentMap源码. 还有sp ...

  6. WCF调用问题

    1---WCF常用的节点绑定 basicHttpBinding和netTcpBinding 当通过Spring对象注入 如 WCFForOrderService 对象注入 public WCFForO ...

  7. IOS创建开源库步骤,提交cocoa pods官网,别人可以使用

    1.打开终端进入某个目录执行  pod lib create BMBlinkButton,按命令步骤执行. 2.目录结构 3.修改BMBlinkButton.podspec文件 4.进入Example ...

  8. URAL 1008 - Image Encoding(bfs坑爹题)

    坑爹题,两种输入输出互相交换,裸bfs #include <stdio.h> #include <string.h> typedef struct { int x; int y ...

  9. [转载] HDFS and Erasure Codes (HDFS-RAID)

    The Hadoop Distributed File System has been great in providing a cloud-type file system. It is robus ...

  10. Linux 挂载iso,并设置为源

    ubuntu在安装lsb-core时需要从 /media/cdrom中查找源,无奈我机器的光驱被我换为硬盘了,无法安装光盘,只有在网上下载的iso文件在硬盘中,所以把iso挂载到它要查找位置 执行: ...