转载请声明: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. boost 特点

    功能强大 跨平台 开源 免费 构造精巧 C++扩展库

  2. 使用正则表达式给网址添加a标签

    在内容中存在链接地址的时候,我们在前台显示时一定想自动的将地址添加上a标签,方便用户进入链接.使用正则表达式就能轻松实现. Jsvascript正则替换 //javascript 正则替换 var s ...

  3. 在linux后台执行脚本

    1. 使用&符号在后台执行命令 你可以在Linux命令或者脚本后面增加&符号,从而使命令或脚本在后台执行,例如:. $ ./my-shell-script.sh & 2. 使用 ...

  4. html 第一阶段 学习使用总结

    基本使用内容: <html> <head> <title>Title of the document</title> <link rel=&quo ...

  5. mockito学习

    mockito学习 写一个测试用例,如果在测试类上面添加了注解@RunWith(SpringJUnit4ClassRunner.class),必须添加@ContextConfiguration(&qu ...

  6. ADSL拨号连接

    dotras是一个提供远程访问服务的组件,使用它可以方便的 进行宽带拨号连接 由于不同的系统中dotras调用的底层api可能不同,所以使用时要根据不同的系统使用不同的dotras. dotras目前 ...

  7. (转)asp.net中Literal与label的区别

    asp.net中Literal与label的区别 一.Literal Web 服务器控件概述(摘于MSDN) 可以使用 Literal Web 服务器控件作为页面上其他内容的容器.Literal 最常 ...

  8. SQL Server 分组后取Top N

    SQL Server 分组后取Top N(转) 近日,工作中突遇一需求:将一数据表分组,而后取出每组内按一定规则排列的前N条数据.乍想来,这本是寻常查询,无甚难处.可提笔写来,终究是困住了笔者好一会儿 ...

  9. 查询数据库返回List<Entity>问题

    如果判断所返回的List<Entity>是否为空不能用 list!=null,因为如果查询数据为空则会返回[],当与null判断的时候会判断为有数据,此时判断条件应该写成list.size ...

  10. 传输层-TCP

    UDP协议提供了端到端之间的通讯,应用程序只需要在系统中监听一个端口,便可以进行网络通讯.随着计算机网络的发展,计算机网络所承载的业务越来越多,有些业务数据的传输需要具备可靠性,譬如我们在进行在线聊天 ...