昨天晚上从源码角度复习了一下android的事件分发机制,今天将笔记整理下放在网上。其实说复习,也是按着《android开发艺术探索》这本书作者的思路做的笔记。

目录

  • 事件是如何从Activity传递到ViewGroup中的
  • ViewGoup对事件做了哪些操作
  • 事件在View中的处理

1. 事件是如何从Activity传递到ViewGroup中的

android的事件分发过程大致为Activity-->ViewGroup-->View,当我们点击屏幕的时候产生事件,系统会调用的ActivitydispatchTouchEvent()开始事件的传播过程,下面我们看一下Activity的dispatchTouchEvent()源码:

public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//当用户操作了按键或者触摸了屏幕,就会回调用该函数
onUserInteraction();
}
//调用window的superDispatchTouchEvent()方法
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}

Activity的dispatchTouchEvent()在它的ACTION_DOWN事件中调用onUserInteraction()这个函数可以用来监听用户的一些按键和触摸操作。然后就会调用window的superDispatchTouchEvent()方法将事件传播到window中,如果window的superDispatchTouchEvent()返回true则代表事件被消耗了,反之代表事件没有被处理,这个时候Activity就会调用自己的onTouchEvent()来处理。

  事件传播到Window中该类是一个抽象类,它的superDispatchTouchEvent()方法是一个抽象方法。我们需要查看它的实现类PhoneWindow中查看事件的执行过程,下面为PhoneWindow的superDispatchTouchEvent()源码:

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//将事件传递给DecorView
return mDecor.superDispatchTouchEvent(event);
}

PhoneWindow的superDispatchTouchEvent()中就一行代码,就是将事件传递给顶层View的superDispatchTouchEvent()。DecorView有将该方法传递给他父类dispatchTouchEvent()。如果读者对DecorView有所了解,应该知道DecorView是FrameLayout的一个子类,也就是说事件传递到了ViewGroup的dispatchTouchEvent()方法中了。

2. ViewGoup对事件做了哪些操作

ViewGroup的dispatchTouchEvent()方法代码很长,我们先看下面一本分代码:


if (actionMasked == MotionEvent.ACTION_DOWN) {
//将之前的状态清除
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
//默认情况下他们两个比较的结果为0
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}

从上面的代码我们可以知道,如果ViewGroup分发的是ACTION_DOWN事件,那么FLAG_DISALLOW_INTERCEPT这个标记位将会被重置,即使子View通过requestDisallowInterceptTouchEvent()设置了该标记为。也就是说分发下来的是ACTION_DOWN事件的话ViewGroup将会调用自己的onInterceptTouchEvent()寻问是否拦截。如果ViewGroup不拦截ACTION_DOWN事件,那么事件将会向它的子View传递:

final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--){
...
//通过dispatchTransformedTouchEvent()将事件传递给子View
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 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;
}
ev.setTargetAccessibilityFocus(false);
}

ViewGroup首先会遍历子所有View,然后判断子元素是否能接受这个点击事件。主要是通过两点,子元素是否在播放动画和点击事件的着落点是否在子元素的区域内。如果满足上面的两点那么事件将会传递给他处理。dispatchTransformedTouchEvent()实际上就是子元素的dispatchTouchEvent()方法。

if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}

如果子元素的dispatchTounchEvnent()返回的为true,那么mFirstTouchTarget将会赋值并且跳出循环,如果为false将会进入下次循环继续遍历子View。

//对mFirstTouchTarget赋值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;

下面为addTouchTarget()函数的代码:

private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
//mFirstTouchTarget为链表结构
mFirstTouchTarget = target;
return target;
}

mFirstTouchTarget是否赋值,将会影响ViewGroup的拦截策略。如果mFirstTouchTarget为null,那么ViewGroup将会拦截下来同一序列的所有事件。那mFirstTouchTarget在什么情况下才为null呢?一般在两种情况下,要么是ViewGroup遍历了所有的子元素事件没有被处理;要么是子元素处理了ACTION_DOWN但是dispatchTouchEvent返回为false。这两种情况下ViewGroup会处理该事件,并且后续的事件也将交给他处理不再向子元素传递。

if (mFirstTouchTarget == null) {
// 出入的第三个参数为null,代表事件交给ViewGroup自己处理
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}

3. 事件在View中的处理

View的事件处理比ViewGroup要简单,他首先会判断是否设置是否设置了onTouchListener()函数。

下面为View的dispatchTouchEvent()部分代码:

public boolean dispatchTouchEvent(MotionEvent event) {
... if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//首先判断是否设置了onTouchListener()
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//调用onTouchEvent(event)
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}

从上面代码可知,如果我们设置了onTouchListener()那么它会调用onTouch()方法,并且onTouch()返回值将会影响对View的onTouchEvent(event)函数的调用,如果返回true将不会调用。由此可见onTouch()的优先级要高于onTouchEvent()。

public boolean onTouchEvent(MotionEvent event) {
...
//如果View设有代理,将会执行TouchDelegate.onTouchEvent(event)
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
//只要View的CLICKABLE和LONG_CLICKABLE有一个返回true,他就会被消耗这个事件。
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
...
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
//点击事件
if (!post(mPerformClick)) {
performClick();
}
}
...
mIgnoreNextUpEvent = false;
break; case MotionEvent.ACTION_DOWN:
...
//长安事件
if (!clickable) {
checkForLongClick(0, x, y);
break;
}
...
break;
...
}
return true;
}
return false;
}

从上面的代码可以看出,View的点击事件是在ACTION_UP事件中调用了performClick()方法处理,长按事件是在ACTION_DOWN事件中调用了checkForLongClick()方法处理。

总结

文章到这里也把android的分发机制从源码的角度分析的差不多了,读者应该对android的事件分发机制应该会有有一个比较全面的了解了。下篇文章我将记录一下关于事件冲突的笔记,如果感兴趣可以继续关注。

参考

Android艺术开发探索第三章————View的事件体系(下)

android事件分发源码分析—笔记的更多相关文章

  1. ViewGroup事件分发源码分析

    1.AndroidStudio源码调试方式 AndroidStudio默认是支持一部分源码调试的,但是build.gradle(app) 中的sdk版本要保持一致, 最好是编译版本.运行版本以及手机的 ...

  2. Android View事件分发源码分析

    引言 上一篇文章我们介绍了View的事件分发机制,今天我们从源码的角度来学习下事件分发机制. Activity对点击事件的分发过程 事件最先传递给当前Activity,由Activity的dispat ...

  3. Qt中事件分发源码剖析

    Qt中事件分发源码剖析 Qt中事件传递顺序: 在一个应该程序中,会进入一个事件循环,接受系统产生的事件,而且进行分发,这些都是在exec中进行的. 以下举例说明: 1)首先看看以下一段演示样例代码: ...

  4. 深入理解Spring系列之十:DispatcherServlet请求分发源码分析

    转载 https://mp.weixin.qq.com/s/-kEjAeQFBYIGb0zRpST4UQ DispatcherServlet是SpringMVC的核心分发器,它实现了请求分发,是处理请 ...

  5. view事件分发源码理解

    有些困难无法逃避,没办法,那就只有去解决它.view事件分发对我而言是一块很难啃的骨头,看了<安卓开发艺术探索>关于这个知识点的讲解,看了好几遍,始终不懂,最终通过调试分析结果,看博客,再 ...

  6. Touch事件分发源码解析

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 以下源码基于Gingerbread 2.3.7 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1.先看ViewGroup的di ...

  7. zeromq源码分析笔记之线程间收发命令(2)

    在zeromq源码分析笔记之架构说到了zmq的整体架构,可以看到线程间通信包括两类,一类是用于收发命令,告知对象该调用什么方法去做什么事情,命令的结构由command_t结构体确定:另一类是socke ...

  8. [Android]Android系统启动流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...

  9. 1.Android 视图及View绘制分析笔记之setContentView

    自从1983年第一台图形用户界面的个人电脑问世以来,几乎所有的PC操作系统都支持可视化操作,Android也不例外.对于所有Android Developer来说,我们接触最多的控件就是View.通常 ...

随机推荐

  1. Django rest framework(7)----分页

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

  2. Cookie熟知

    Cookie能干什么 会话(cookie,session)技术的一种.因为http协议是无状态的,每次都是基于一个请求一个响应.每次请求和响应都跟上次没有关系.我们需要记录之前对话信息.cookie技 ...

  3. vue技术分享-你可能不知道的7个秘密

    前言 本文是vue源码贡献值Chris Fritz在公共场合的一场分享,觉得分享里面有不少东西值得借鉴,虽然有些内容我在工作中也是这么做的,还是把大神的ppt在这里翻译一下,希望给朋友带来一些帮助. ...

  4. servlet3.0 新特性——异步处理

    Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下: 首先,Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理: 接着,调用业务接口的某些方法,以完成业 ...

  5. BigDecimal.setScale 处理java小数点

    BigDecimal.setScale()方法用于格式化小数点setScale(1)表示保留一位小数,默认用四舍五入方式 setScale(1,BigDecimal.ROUND_DOWN)直接删除多余 ...

  6. python字符串操作实方法大合集

    python字符串操作实方法大合集,包括了几乎所有常用的python字符串操作,如字符串的替换.删除.截取.复制.连接.比较.查找.分割等,需要的朋友可以参考下:   #1.去空格及特殊符号 s.st ...

  7. 数据分析之---Python可视化工具

    1. 数据分析基本流程 作为非专业的数据分析人员,在平时的工作中也会遇到一些任务:需要对大量进行分析,然后得出结果,解决问题. 所以了解基本的数据分析流程,数据分析手段对于提高工作效率还是非常有帮助的 ...

  8. 【手记】走近科学之为什么明明实现了IEnumerable<T>的类型却不能调用LINQ扩展方法

    比如Json.NET的JObject明明实现了IEnumerable<T>,具体来说是IEnumerable<KeyValuePair<string, JToken>&g ...

  9. hackathon活动复盘

    复盘: hackathon技术创新型.理念创新落地型评委的一个评估点:在公司怎么落地的问题5分钟的demonstration,要进行测试,5分钟很快的:表达方式:common language,让人能 ...

  10. 解决openfire中发送某些特殊字符会断开xmpp连接的问题

    在openfire中,如果发送某些特殊的字符(例如一些表情符合),会断开xmpp的连接,经查,是由以下的代码问题引起的: src\java\org\jivesoftware\openfire\net\ ...