版权声明:本文出自汪磊的博客,转载请务必注明出处。

在前两篇我们共同探讨了事件传递机制《View篇》《ViewGroup篇》,我们知道View触摸事件是ViewGroup传递过去的,比如一个很简单的布局最外层是LinearLayout,里面就一个Button,我们点击Button的时候触摸事件是由外层LinearLayout传递给里面Button的,但是有没有想过当前触摸事件是谁传递给外层的LinearLayout的呢?带着这个疑问我们继续来共同探讨一下。

从Demo示例说起

我们还是先写一个简单的demo,很简单,代码如下:自定义Button:

 public class MyButton extends Button {

     private final String TAG = "WL";

     public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
} @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//
Log.i(TAG, "MyButton_dispatchTouchEvent_Action:"+ev.getAction());
return super.dispatchTouchEvent(ev);
} @Override
public boolean onTouchEvent(MotionEvent event) {
//
Log.i(TAG, "MyButton_onTouchEvent_Action:"+event.getAction());
return super.onTouchEvent(event);
}
}

自定义LinearLayout:

 public class MyLinearLayout extends LinearLayout {

     private final String TAG = "WL";

     public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
//
} @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//
Log.i(TAG, "MyLinearLayout_dispatchTouchEvent_Action:"+ev.getAction());
return super.dispatchTouchEvent(ev);
} @Override
public boolean onTouchEvent(MotionEvent event) {
//
Log.i(TAG, "MyLinearLayout_onTouchEvent_Action:"+event.getAction());
return super.onTouchEvent(event);
}
}

布局文件:

 <com.wl.activitydispatchtouchevent.MyLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0099cc"
android:id="@+id/mylinearlayout"
android:gravity="center"
tools:context="com.wl.activitydispatchtouchevent.MainActivity" > <com.wl.activitydispatchtouchevent.MyButton
android:id="@+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="WL_Button" /> </com.wl.activitydispatchtouchevent.MyLinearLayout>

Activity中代码:

 public class MainActivity extends Activity implements OnClickListener,
OnTouchListener { private final String TAG = "WL"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fullscreen);
//
findViewById(R.id.mybutton).setOnClickListener(this);
findViewById(R.id.mybutton).setOnTouchListener(this);
//
findViewById(R.id.mylinearlayout).setOnClickListener(this);
findViewById(R.id.mylinearlayout).setOnTouchListener(this);
} @Override
public boolean onTouch(View v, MotionEvent event) {
//
Log.i(TAG, "onTouch___v:" + v + "___action:" + event.getAction());
return false;
} @Override
public void onClick(View v) {
//
Log.i(TAG, "onClick___v:" + v);
} @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG, "MainActivity__dispatchTouchEvent__action:" + ev.getAction());
return super.dispatchTouchEvent(ev);
} @Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "MainActivity___onTouchEvent___action=" + event.getAction());
return super.onTouchEvent(event);
}
}

怎么样,很简单吧。和上一篇讲解ViewGroup传递机制的Demo几乎差不多,主要差别就是在Activity中我们重写了Activity的dispatchTouchEvent与onTouchEvent方法。

我们看一下运行效果,点击Button,打印信息如下:

 MainActivity__dispatchTouchEvent__action:0
MyLinearLayout_dispatchTouchEvent_Action:0
MyButton_dispatchTouchEvent_Action:0
onTouch___v:com.wl.activitydispatchtouchevent.MyButton___action:0
MyButton_onTouchEvent_Action:0
MainActivity__dispatchTouchEvent__action:1
MyLinearLayout_dispatchTouchEvent_Action:1
MyButton_dispatchTouchEvent_Action:1
onTouch___v:com.wl.activitydispatchtouchevent.MyButton___action:1
MyButton_onTouchEvent_Action:1
onClick___v:com.wl.activitydispatchtouchevent.MyButton

除去与Activity有关的信息,其余信息打印顺序相信你应该轻松理解了。我们看到触摸事件实现传递到Activity中的,其次才传递到MyLinearLayout,最后传递给MyButton。是不是触摸事件就是Activity先获取到接下来才继续向下传递的呢?别急着下结论,我们看看Activity中dispatchTouchEvent都做了什么。

Activity事件传递机制源码分析(源码版本为API23

Activity中dispatchTouchEvent方法源码如下:

  public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}

是不是爽歪歪?这么短,我们看2-4行代码,首先判断如果是ACTION_DOWN事件则执行onUserInteraction()方法,对于onUserInteraction()方法这里不做具体分析,不是本篇重点。

我们继续向下分析,5-9代码,如果if条件成立则直接返回true,不成立则dispatchTouchEvent最终返回值由onTouchEvent决定,那么if判断就是关键了。

5行代码,getWindow()返回mWindow对象,在Activity的attach方法中进行的初始化,如下:

     final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) { ...........
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
...........
}
mWindow其实就是PhoneWindow对象,接下来我们找到PhoneWindow类(源码目录:...\sdk\sources\android-23\com\android\internal\policy\)。
PhoneWindow类继承自Window类,我们先看看父类中superDispatchTouchEvent是怎么处理的。
Window类中superDispatchTouchEvent源码如下:
1  /**
* Used by custom windows, such as Dialog, to pass the touch screen event
* further down the view hierarchy. Application developers should
* not need to implement or call this.
*
*/
public abstract boolean superDispatchTouchEvent(MotionEvent event);

看到了吧,很简单,父类中就是一个抽象方法, 看注释就知道此方法主要用来屏幕事件传递的,开发者不需要实现或者调用这个方法。

接下来我们看看PhoneWindow类中的superDispatchTouchEvent方法:

  @Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}

是不是更简单?直接调用mDecor的superDispatchTouchEvent方法,mDecor是什么玩意呢?这里就直说说了,mDecor是DecorView的实例。

DecorView类是PhoneWindow类的内部类,源码如下:

 private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {

    ..........
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
} ..........
}

我勒个去,搞半天DecorView 继承自FrameLayout,我们知道 FrameLayout继承自ViewGroup,最终就是调用ViewGroup中的dispatchTouchEvent方法进行事件分发。

但是到这里我们还有一个疑问,以我们Demo为例,通过上述分析事件先传递到Activity的dispatchTouchEvent方法,然后调用DecorView 的superDispatchTouchEvent方法最终调用的ViewGroup的dispatchTouchEvent方法,但是跟我们Demo中的MyLinearLayout有什么关系呢?或者说是怎么传递到MyLinearLayout的呢?

要解答这个疑问我们就必须熟知我们平时调用Activity中的setContentView方法设置布局的时候我们自己的布局到底是怎么挂载到Activity上的,这篇我们就不进入深入源码解析了,不是本篇重点,直说一些结论性东西。后续会单独写一篇文章专门分析setContentView究竟都做了什么。

我们在调用setContentView设置布局的时候其实都是被放置在id为content的FrameLayout 布局中的,注意id为content的FrameLayout 布局并不是上面讲的DecorView,具体层级关系如下:

看到了吧,id为content的FrameLayout 布局是DecorView的子View布局。我们自己的布局最后总会替换掉id为content的FrameLayout 。

到这里你该明白了吧,Activity将触摸事件经过层层传递给DecorView, 而DecorView会调用ViewGroup的dispatchTouchEvent方法将事件传递给子View。之后的逻辑就是我们上两篇所讲的内容了。

接下来我们回看Activity中dispatchTouchEvent方法,第5行根据我们上述分析的,如果最终找到子View消耗事件则返回值为true,进而整个方法返回true。如果没有子View处理当前触摸事件则返回false,执行Activity中onTouchEvent方法。

我们接下来分析一下Activity中onTouchEvent方法,源码如下;

 /**
* Called when a touch screen event was not handled by any of the views
* under it. This is most useful to process touch events that happen
* outside of your window bounds, where there is no view to receive it.
*
* @param event The touch screen event being processed.
*
* @return Return true if you have consumed the event, false if you haven't.
* The default implementation always returns false.
*/
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
} return false;
}

逻辑也不复杂,主要就是第12行代码,调用mWindow的shouldCloseOnTouch方法,如果此方法返回true则整个方法返回true,反之则返回false。

mWindow上面我们分析过就是PhoneWindow的实例,好了我们就去PhoneWindow类中找shouldCloseOnTouch方法看一下吧,然而PhoneWindow中并没有这个方法,那我们看看父类Window中有没有这个方法呢,果然这个方法在其父类中找到,源码如下:

 public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
return true;
}
return false;
}

这里主要逻辑也是一个判断,判断mCloseOnTouchOutside标记以及是否为ACTION_DOWN事件,然后判断点击事件的坐标x,y有没有超出边界,最后调用peekDecorView()判断是否为空。peekDecorView()在Window类中就是一个抽象方法,具体实现在PhoneWindow类中如下:

     public final View peekDecorView() {
return mDecor;
}

很简单,就是返回mDecor,上面我们分析过mDecor就是DecorView的实例,这里我们需要知道我们在Activity中调用setContentView的时候mDecor就会初始化,具体分析会在下一篇文章中,这里只要知道mDecor不为null就可以了。

其余的都不难理解但是mCloseOnTouchOutside 是个什么鬼呢?我们知道Activity设置成Dialog样式的时候默认点击外部的时候是会关闭的,同样我们也可以调用setFinishOnTouchOutside(false)设置为点击外部时候Activity不关闭,mCloseOnTouchOutside 就是用来记录这个的,如果我们将Activity设置为Dialog样式mCloseOnTouchOutside 默认就被设置为true,我们知道大部分情况下Activity是不会设置为Dialog样式的,所以mCloseOnTouchOutside 默认为false。(关于mCloseOnTouchOutside其实是想从源码角度分析一下的,但是这部分内容实在和传递机制不沾边,就这部分有一个判断,所以就不仔细分析了,在下一篇分析setContentView的时候在提一下吧 )

这里我们稍微总结一下:mCloseOnTouchOutside 默认情况下是false,如果Activity样式设置为Dialog系统默认会将mCloseOnTouchOutside 设置为true,所以Dialog样式的Activity默认情况下点击外部会关闭,如果我们调用setFinishOnTouchOutside(false)或者在styles文件中设置了 <item name="android:windowCloseOnTouchOutside">false</item> 那么最终都会将mCloseOnTouchOutside 变量置为false,点击Activity外部也就不会关闭了。

综上分析,Window中shouldCloseOnTouch大多数情况下是返回false的,从而Activity中onTouchEvent大多说情况下也是返回false,除非我们进行了特殊设置。这也就是Activity中onTouchEvent注释是The default implementation always returns false而不是The default implementation returns false,就多了一个always。

好了,到此为止关于安卓事件传递机制最重要的部分都已经讲解完毕,最最核心的还是要掌握View以及ViewGroup的部分,至于Activity的传递大体了解一下流程就可以了。

下一篇我们一起探究一下Activity中setContentView方法究竟做了什么事情。反过来你能更好理解本篇中的某些点。

Android事件传递机制详解及最新源码分析——Activity篇的更多相关文章

  1. Android事件传递机制详解及最新源码分析——ViewGroup篇

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 在上一篇<Android事件传递机制详解及最新源码分析--View篇>中,详细讲解了View事件的传递机制,没掌握或者掌握不扎实的小伙伴 ...

  2. Android事件传递机制详解及最新源码分析——View篇

    摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处. 对于安卓事件传递机制相信绝大部分开发者都听说过或者了解过,也是面试中最常问的问题之一.但是真正能从源码角度理解具体事件传递流程的相信并不多, ...

  3. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  4. Android 的事件传递机制,详解

    Android 的事件传递机制,详解 前两天和一个朋友聊天的时候.然后说到事件传递机制.然后让我说的时候,忽然发现说的不是非常清楚,事实上Android 的事件传递机制也是知道一些,可是感觉自己知道的 ...

  5. Android Touch事件传递机制详解 下

    尊重原创:http://blog.csdn.net/yuanzeyao/article/details/38025165 资源下载:http://download.csdn.net/detail/yu ...

  6. 《Android NFC 开发实战详解 》简介+源码+样章+勘误ING

    <Android NFC 开发实战详解>简介+源码+样章+勘误ING SkySeraph Mar. 14th  2014 Email:skyseraph00@163.com 更多精彩请直接 ...

  7. Android事件分发机制详解

    事件分发机制详解 一.基础知识介绍 1.经常用的事件有:MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP等 2 ...

  8. Android Touch事件传递机制详解 上

    最近总是遇到关于Android Touch事件的问题,如:滑动冲突的问题,以前也花时间学习过Android Touch事件的传递机制,可以每次用起来的时候总是忘记了,索性自己总结一下写篇文章避免以后忘 ...

  9. Android事件分发机制详解(1)----探究View的事件分发

    探究View的事件分发 在Activity中,只有一个按钮,注册一个点击事件 [java] view plaincopy button.setOnClickListener(new OnClickLi ...

随机推荐

  1. 【Python】debug工具-pdb(转)

    Debug功能对于developer是非常重要的,python提供了相应的模块pdb让你可以在用文本编辑器写脚本的情况下进行debug. pdb是python debugger的简称. 常用的一些命令 ...

  2. 人工智能(AI)库TensorFlow 踩坑日记之二

    上次 踩坑日志之一 遗留的问题终于解决了,所以作者(也就是我)终于有脸出来写第二篇了. 首先还是贴上 卷积算法的示例代码地址 :https://github.com/tensorflow/models ...

  3. Tornado 判断用户登录状态和操作权限(装饰器)

    判断是否登录: def authenticated(method): '''''' @functools.wraps(method) def wrapper(self, *args, **kwargs ...

  4. 超超超简单的bfs——POJ-3278

    Catch That Cow Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 89836   Accepted: 28175 ...

  5. Open-Falcon第五步安装Query(小米开源互联网企业级监控系统)

    安装Query query组件,绘图数据的查询接口,query组件收到用户的查询请求后,会从后端的多个graph,查询相应的数据,聚合后,再返回给用户. cd /usr/local/open-falc ...

  6. vue指令v-else-if示例解析

    表示 v-if 的 "else if 块".可以链式调用. <div id="app"> <p v-if="isRender&quo ...

  7. 初笔,JAVA.HelloWorld代码详解

    HelloWorld.java //文件名 public class HelloWorld{ public static void main(String[] args){ System.out.pr ...

  8. 手算平方根和基于 Java BigInteger 的大整数平方根的实现

    为了实现任意大数的运算,long用BigInteger替换带哦. 好了废话少数,先说数学原理,也就是手算平方根计算机代码实现!那么什么叫手算平方根了??? 手开方图解 据说前苏联的普通工人都会的(毛熊 ...

  9. Cox回归模型【生存分析】

    参考:<复杂数据统计方法--基于R的应用> 吴喜之 在生存分析中,研究的主要对象是寿命超过某一时间的概率.还可以描述其他一些事情发生的概率,例如产品的失效.出狱犯人第一次犯罪.失业人员第一 ...

  10. 【复制】【编码】MySQL复制中的编码问题

    编码背景知识 Latin-1,全称ISO 8859-1 Latin 1 对ASCII的拉丁语扩展 向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x8 ...