Android 带你读懂事件分发
工作有一段时间,有必要掌握事件传递的机制,最近研究了一下,记录下心得。
1 Android中的事件
android中触摸事件比较多,封装中MotionEvent类中,点击、触摸、滑动是我们常用的事件
- MotionEvent.ACTION_DOWN
- MotionEvent.ACTION_MOVE
- MotionEvent.ACTION_UP
- MotionEvent.ACTION_CANCEL
2 核心方法
了解传递机制前,我们需要先学习几个方法。
2.1ViewGroup
- dispatchTouchEvent 分发事件,默认接受事件并往下分发。如果返回true,拦截事件,不分发。下同。
- onInterceptTouchEvent 预处理事件 默认返回false ,返回true表示拦截
- onTouchEvent 处理事件 默认返回false ,不消费事件。如果在根布局或代码中设置了clickable=true,则super.onTouchEvent返回true,表示消费了事件。
2.2View
- dispatchTouchEvent
- onTouchEvent 处理事件 返回值需要看view的属性clickable==true
2.3View/ViewGroup 的 onTouchEvent返回值:
- 如果view/viewGroup可点击的,比如Button,clickable = true,则super.onTouchEvent 返回true
- 如果view/viewGroup不可点击,比如TextView,clickable = false,则super.onTouchEvent 返回false
- 在xml中设置view/viewGroup clickable属性为true, 则该view:super.onTouchEvent 返回true
2.4核心
- clickable影响了super.onTouchEvent返回值
- 如果返回false,View只能处理down事件,后续事件不会触发
- 如果返回true,View的down\move\up都能触发
3 事件传递
一次完整的屏幕触摸事件:手指按下,滑动,抬起。这个事件由一个action_down,若干个action_move,和一个action_up组成。那么它在我们的屏幕中是如何传递的呢?
默认情况下事件传递是从Activity到ViewGroup到View的过程,最终由View接受到。如图:

我们定义一个MyViewGroup和一个MyView,打印其中的方法。
MyViewGroup,clickable为false,super.onTouchEvent默认为false,不消费事件
public class MyViewGroup extends FrameLayout {
String Tag = "===MyViewGroup";
public MyViewGroup(Context context) {
super(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(Tag,"dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(Tag,"dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(Tag,"dispatchTouchEvent ACTION_UP");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(Tag, "onInterceptTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(Tag, "onInterceptTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(Tag, "onInterceptTouchEvent ACTION_UP");
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(Tag, "onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(Tag, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(Tag, "onTouchEvent ACTION_UP");
break;
}
return super.onTouchEvent(ev);
}
}
MyView 继承TextView,clickable默认false,super.onTouchEvent默认为false,不消费事件
public class MyView extends android.support.v7.widget.AppCompatTextView {
String Tag = "===MyView";
public MyView(Context context) {
super(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(Tag,"dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(Tag,"dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(Tag,"dispatchTouchEvent ACTION_UP");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(Tag,"onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(Tag,"onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(Tag,"onTouchEvent ACTION_UP");
break;
}
return super.onTouchEvent(ev);
}
}
activity布局中引用
<com.zcwipe.frecyclerviewdemo.MyViewGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"> <com.zcwipe.frecyclerviewdemo.MyView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/color_theme"
android:gravity="center"
android:text="content" />
</com.zcwipe.frecyclerviewdemo.MyViewGroup>
事件都是由action_down开始,
首先activity执行super.dispatchTouchEvent分发给viewgroup。
然后viewgroup执行super.dispatchTouchEvent,super.onInterceptTouchEvent。
最后view执行super.dispatchTouchEvent,onTouchEvent。这个时候view在onTouchEvent里就接受到了action_down事件。
后续的action_move,action_up事件不再传递给ViewGroup和View。直接由activity处理。

如果拦截,viewgroup的action_move返回true,拦截事件,
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(Tag, "onInterceptTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(Tag, "onInterceptTouchEvent ACTION_MOVE");
return true;
case MotionEvent.ACTION_UP:
Log.e(Tag, "onInterceptTouchEvent ACTION_UP");
break;
}
return super.onInterceptTouchEvent(ev);
}
后续move,up动作由viewgroup的onTouchEvent处理,且不再执行onInterception方法

情况3:
如果viewgroup拦截down事件,那么down事件不再传递给子view,直接由viewgroup的ontouch处理。在viewgroup的onTouchEvent事件中:
如果消费了down事件,返回true,那么后续事件move,up都会直接交由viewgroup的onTouchEvent处理,且不再执行viewgroup的onInterceptTouchEvent方法。日志输出:

如果没有消费,那么move,up将由上层view处理。
案例:加入我们有这么一个需求,在自定义viewgroup中接受action_move事件,实现滑动效果,如何处理?
由情况1我们知道,如果view不消费down事件,viewgroup也不消费事件,那么无法实现。
由情况2我们知道,如果view消费了down事件,viewgroup也不拦截,那么也无法实现。
实现方案两种:
* view onTouchEvent中down消费,viewgroup:onInterceptTouchEvent中move拦截
* view onTouchEvent中down不消费 ,viewgroup:onTouchEvent中down消费
有时候我们不知道view是否消费了事件,那么使用以下方案写出健壮行代码,viewgroup中
1. ontouchevent down消费事件
2. onInterceptTouchEvent中move拦截
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(Tag, "onInterceptTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
return true;
// break;
case MotionEvent.ACTION_UP:
Log.e(Tag, "onInterceptTouchEvent ACTION_UP");
break;
}
return super.onInterceptTouchEvent(ev);
} @Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(Tag, "onTouchEvent ACTION_DOWN");
return true;
case MotionEvent.ACTION_MOVE:
Log.e(Tag, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(Tag, "onTouchEvent ACTION_UP");
break;
}
return super.onTouchEvent(ev);
}
这样可以稳定的在viewgroup中处理action_move事件,且不影响子view的点击事件。日志输出:

Android 带你读懂事件分发的更多相关文章
- (转载) Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框
Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框 标签: Android清除功能EditText仿IOS的输入框 2013-09-04 17:33 70865人阅读 ...
- Android高级_视频播放控件
一.Android系统自带VideoView控件 1. 创建步骤: (1)自带视频文件放入res/raw文件夹下: (2)声明初始化VideoView控件: (3)创建视频文件Uri路径,Uri调用p ...
- 一文带你读懂什么是vxlan网络
一个执着于技术的公众号 一.背景 随着云计算.虚拟化相关技术的发展,传统网络无法满足大规模.灵活性要求高的云数据中心的要求,于是便有了overlay网络的概念.overlay网络中被广泛应用的就是vx ...
- JS调用Android、Ios原生控件
在上一篇博客中已经和大家聊了,关于JS与Android.Ios原生控件之间相互通信的详细代码实现,今天我们一起聊一下JS调用Android.Ios通信的相同点和不同点,以便帮助我们在进行混合式开发时, ...
- Android Material适配 为控件设置指定背景色和点击波纹效果
Android Material适配 为控件设置指定背景色和点击波纹效果,有需要的朋友可以参考下. 大部分时候,我们都需要为控件设置指定背景色和点击效果 4.x以下可以使用selector,5.0以上 ...
- android中的EditView控件
android中的EditView控件 EditText继承关系:View-->TextView-->EditText ,EditText是可编辑文本框 1.EditText默认情况下,光 ...
- Android 5.0新控件——FloatingActionButton(悬浮按钮)
Android 5.0新控件--FloatingActionButton(悬浮按钮) FloatingActionButton是5.0以后的新控件,一个悬浮按钮,之所以叫做悬浮按钮,主要是因为自带阴影 ...
- Android开发:文本控件详解——TextView(一)基本属性
一.简单实例: 新建的Android项目初始自带的Hello World!其实就是一个TextView. 在activity_main.xml中可以新建TextView,从左侧组件里拖拽到右侧预览界面 ...
- Android-事件分发(ViewGroup)
http://blog.csdn.net/guolin_blog/article/details/9153747 http://blog.csdn.net/lmj623565791/article/d ...
随机推荐
- layer单选框 radio的问题总结
放官方文档: 位置 页面元素-表单:内置模块-表单属性title可自定义文本属性disabled开启禁用设置value="xxx"可自定义值,否则选中时返回的就是默认的onradi ...
- 变分推断到变分自编码器(VAE)
EM算法 EM算法是含隐变量图模型的常用参数估计方法,通过迭代的方法来最大化边际似然. 带隐变量的贝叶斯网络 给定N 个训练样本D={x(n)},其对数似然函数为: 通过最大化整个训练集的对数边际似然 ...
- uestc summer training #9 牛客第三场 BFS计数
G.coloring tree BFS计数 题目:给你n(<=5000)个节点的一颗树 你有K(<=5000)种颜色 你可以给每一个节点染一种颜色 总共有Kn种染色方法 在一种染色方法中 ...
- java代码中的坑
1.Integer 类型数值比较: public static void main(String[] args) { // TODO Auto-generated method stu ...
- Restricting Input in HTML Textboxes to Numeric Values
Ok, here’s a fairly basic one – how to force a textbox to accept only numeric input. Somebody asked ...
- 同步windows时间到linux服务器
输入date -R 查看系统时间 输入命令 ntpdate time.windows.com 同步windows时间到linux
- [易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理]
[易学易懂系列|rustlang语言|零基础|快速入门|(20)|错误处理] 实用知识 错误处理 我们今天来讲讲Rust中的错误处理. 很多语言都有自己的错误处理方式,比如,java是异常处理机制. ...
- 【BZOJ2752】【Luogu P2221】 [HAOI2012]高速公路
不是很难的一个题目.正确思路是统计每一条边被经过的次数,但我最初由于习惯直接先上了一个前缀和再推的式子,导致极其麻烦难以写对而且会爆\(longlong\). 推导过程请看这里. #include & ...
- 如何在某些情况下禁止提交Select下拉框中的默认值或者第一个值(默认选中的就是第一个值啦……)
群里有个帅哥问了这么个问题,他的下拉框刚进页面时是隐藏起来的,但是是有值的,为啥呢?因为下拉框默认选中了第一个值呗,,, 所以提交数据的时候就尴尬啦,明明没有选,但是还是有值滴.怎么办呢? 一开始看到 ...
- Session中的方法
Session 管理一个数据库的任务单元,即管理数据库中的增删改查操作,提交事务. 方法CRUD:save(),delete(),load(),get(),update(),saveOrUpdate( ...