Android View的事件分发机制解析
作者:网易·周龙
最近刚看完android-Ultra-Pull-To-Refresh下拉刷新的源码,发现在写自定义控件时,对于View的事件的传递总是搞不太清楚,而View事件的分发机制,又是解决可能出现的滑动冲突的基础,因此专门学习了一下,结合一个例子来好好分析分析。
先上Demo,很简单,就是一个自定义ViewGroup和一个自定义View。View分发机制中,有三个很重要的方法是:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent,它们分别对应着事件的分发、拦截和消耗,因此我在自定义View的这三个方法里加上Log,根据打印的日志来分析下具体的流程。
public class CustomViewGroup extends LinearLayout { private static final String TAG = "Mylog"; public CustomViewGroup(Context context) { super(context);} public CustomViewGroup(Context context, AttributeSet attrs) { super(context, attrs);} @Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.d(TAG, "customViewGroup.dispathcTouchEvent "+ev.toString()); return super.dispatchTouchEvent(ev);} @Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.d(TAG, "customViewGroup.onInterceptTouchEvent "+ev.toString()); return super.onInterceptTouchEvent(ev);// return true;} @Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d(TAG, "customViewGroup.onTouchEvent "+event.toString()); return super.onTouchEvent(event);// return true;} @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b);}
}public class CustomView extends View { private static final String TAG = "Mylog"; public CustomView(Context context) { super(context);} public CustomView(Context context, AttributeSet attrs) { super(context, attrs);} public CustomView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);} @Overridepublic boolean dispatchTouchEvent(MotionEvent event) {Log.d(TAG, "customview.dispatchtouchevent "+event.toString()); return super.dispatchTouchEvent(event);} @Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d(TAG, "customview.onTouchEvent "+event.toString());// return true;return super.onTouchEvent(event);}
}customView.setOnTouchListener(new View.OnTouchListener() { @Overridepublic boolean onTouch(View v, MotionEvent event) {Log.d(TAG, "customView.onTouch "+event.toString()); return false;}});customViewGroup.setOnTouchListener(new View.OnTouchListener() { @Overridepublic boolean onTouch(View v, MotionEvent event) {Log.d(TAG, "customViewGroup.onTouch "+event.toString()); return false;}});} @Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.d(TAG, "Activity.dispatchTouchEvent "+ev.toString()); return super.dispatchTouchEvent(ev);} @Overridepublic boolean onTouchEvent(MotionEvent event) {Log.d(TAG, "Activity.onTouchEvent "+event.toString()); return super.onTouchEvent(event);}
在Activity界面,我对CustomView和CustomViewGroup设定了onTouch监听。
下图是界面的一个示意图,Activity的界面包含了一个ViewGroup,而ViewGroup里包含了一个View,最简单的线性布局。
事件传递的基本原则:对于一个根ViewGroup来说,点击事件产生后,首先会传递给自身,并调用其dispatchTouchEvent方法,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着调用onTouchEvent来处理这个事件;如果这个ViewGroup不拦截,则将事件传递给子元素,子元素调用其onInterceptTouchEvent方法,如此反复直到最终被处理。
同一事件序列:指手指接触屏幕起,到手指离开屏幕的那一刻,在这个过程中产生的一系列动作。一般以down事件开始,一系列move事件,并以一个up事件结束。
可以表示为:[down, move,move,move......up]
在上述的基本原则下,我们来根据例子分析下View的事件分发过程。
在默认情况下,点击区域3 View,Activity消耗事件
打印出来的Log如上,事件ACTION_DOWN先从Activity的dispatchTouchEvent开始传递,向下分发,由于Activity和ViewGroup默认都是不拦截事件的,因此不会拦截,一直传递到最内侧的View,注意到View的onTouch方法比其onTouchEvent方法先调用,这表示onTouch具有更高的优先级。由于我默认设置view也不处理事件,因此事件最终回到了Activity的onTouchEvent中,并接受了ACTION_UP事件。这表明一旦一个View接受了ACTION_DOWN事件,那么接下来的同一序列的事件都将交给它处理。结合下图可以更好的来理解。
内部View消耗ACTION_DOWN事件
将代码中,CustomView的onTouchEvent返回值为true,则表示可以对接受到的事件进行消耗。我们点击区域3,并滑动一段距离,此时打印log如下:
可以看到,当View的onTouchEvent返回true时,当ACTION_DOWN事件分发到View后,就不在会向父层转发,表示此View将会对事件经行处理,之后的ACTION_MOVE等同一序列的事件,都将沿着之前的路径转发给此View。
外部ViewGroup消耗ACTION_DOWN事件
将CustomView的onTouchEvent返回改成super.onTouchEvent,并将CustomViewGroup中的onTouchEvent和onInterceptTouchEvent返回均设为true,表示将由ViewGroup来拦截事件,并进行处理。我们点击内部的区域3 View:得到的打印logo如下:
从log中,我们可以发现,一旦ViewGroup准备拦截事件,则事件将不再被转发给View,因此View的dispatchTouchEvent等函数均不会被触发,另外,在之后的同一序列事件中,ViewGroup的onInterceptTouchEvent函数也不会被触发,这表示:一旦某个ViewGroup决定拦截事件,则系统不再会询问接下来的事件ViewGroup是否要拦截。
点击区域2 ViewGroup
这种情况比较简单,此时因为点击的区域中,不包含其他View,因此ViewGroup将不会把事件转发给ViewGroup中的其他子View。而在Activity和ViewGroup中正常的进行事件分发,这里给出打印的logo,流程图示意省略。
事件经过View后,在由ViewGroup处理
这种情况相对少见一点。我们需要将ViewGroup的onInterceptTouchEvent设为super,onTouchEvent设为true,View的onTouchEvent设为super. 这保证了事件将会被传递给子View,但子View不会去消耗这个事件。同样点击区域3 View后,logo如下:
和预期的一致,事件正常的分发到了View当中,但是子View并没有处理事件,而是交回到ViewGroup来处理。值得注意的是,之后同一序列的其他事件,都不在会分发给View,而是直接交给ViewGroup的onTouch方法去处理。这意味着一个View一旦接收了事件的转发而不处理,那么系统将不会把同一序列的其他事件转发给它。
大概情况就这么几种,常见的一般是直接由ViewGroup来消耗或者View消耗事件。
这里主要还是从应用层表面来分析了一些View事件的分发过程,如果想深入了解各个函数的实现,可以去阅读一下View的源码,从而更好的证实上述的结果。另外,注意到,View(不是ViewGroup)是不包含onInterceptTouchEvent方法的,这表示只要有事件传递给View,其onTouchEvent方法就会被触发,而ViewGroup是默认不拦截任何事件的,其onInterceptTouchEvent方法默认返回false。
在了解了View的事件分发机制后,就可以对一些出现滑动冲突的情况进行自己的处理,之后的文章中会继续写写学习心得。
· END ·
网易云信∣真正稳定的IM云服务
ID:neteaseim 长按关注,精彩不断
Android View的事件分发机制解析相关推荐
- Android View的事件分发机制和滑动冲突解决方案
这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...
- 安卓自定义View进阶-事件分发机制原理【转自 app架构师 微信公众号】
注意:本文中所有源码分析部分均基于 API23(Android 6.0) 版本,由于安卓系统源码改变很多,可能与之前版本有所不同,但基本流程都是一致的. 为什么要有事件分发机制? 安卓上面的View是 ...
- View体系与自定义View(三)—— View的事件分发机制
1. 分析Activity的构成 一个Activity包含一个Window对象,这个对象是由PhoneWindow来实现的.PhoneWindow将DecorView作为整个应用窗口的根View. 而 ...
- 安卓自定义View进阶-事件分发机制原理
之前讲解了很多与View绘图相关的知识,你可以在 安卓自定义View教程目录 中查看到这些文章,如果你理解了这些文章,那么至少2D绘图部分不是难题了,大部分的需求都能满足,但是关于View还有很多知识 ...
- 自定义View(二)--表层浅析View的事件分发机制和滑动冲突
转载请注明出处:From李诗雨:http://blog.csdn.net/cjm2484836553/article/details/54387722 不诗意的女程序猿不是好厨师~ 这篇文章来得有些曲 ...
- Android View体系(五)从源码解析View的事件分发机制
Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Sc ...
- 安卓自定义View进阶-事件分发机制详解
原文地址:http://www.gcssloop.com/customview/dispatch-touchevent-source Android 事件分发机制详解,在上一篇文章 事件分发机制原理 ...
- Android事件分发机制解析
博客地址juexingzhe欢迎关注 今天结合流程图和代码来对Android事件分发机制做一个总结,我自己起一个叫法就是"3个3". 跟事件分发相关的主要有三个节点方法: 1.di ...
- 对Android view/viewgroup事件分发的理解
首先看看讲事件分发的博客: http://blog.csdn.net/xiaanming/article/details/21696315 和 http://www.csdn123.com/html/ ...
最新文章
- java sql sum函数的使用方法_SQL常用汇总函数用法说明
- 你一定要了解的NB-IoT !
- zip版mysql5.6_mysql 5.6 压缩包版安装方法
- 培训课程第三期签到和意见发表
- HTML颜色代码表/颜色名(网摘)
- 我对C++的一些疑问
- SQL2008数据类型
- [转载] python下载安装教程
- 【转载】Android S5PV210 fimc驱动分析 - fimc_regs.c
- c语言 对排两个答案文件
- Matplotlib笔记(莫烦Python)
- 【手把手教安装】VUE安装教程!!!
- Github渗透测试工具库
- RAC修改IP(public/virtual/scan)
- SubmitButton
- 华为认证hcip怎么找工作?考取华为认证hcip证书可以做什么?
- bzoj1050 [HAOI2006]旅行comf(并查集)
- typora生成目录
- 预测移动用户人口属性的Kaggle竞赛作品解析
- 嵩天老师《Python语言程序设计》第6周测试题笔记
热门文章
- 各浏览器抗uaf机制
- [Leetcode][JAVA] Reorder List
- 如何将数据导入到 SQL Server Compact Edition 数据库中(四)
- zabbix frontends php,zabbix 3.0.3 安装
- R语言教程:生存分析
- 目标检测--边界框(bounding box)解析
- Protues 8.6 详细安装步骤
- java同步锁实例_Java lock同步锁使用实例解析
- 编程之美系列之一——阶乘的运算
- linux tar压缩包目录,如何在Linux上使用tar命令解压和压缩文件