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 ...
随机推荐
- countUp.js-数字滚动效果(简单基础使用)
最近写了个移动端宣传页,里面有数字的效果,所以有使用到countUp.js. 以下内容有包括:h5页面countUp.js的引入和实例.参数说明.事件简单使用和描述.countUp.js源代码. 附上 ...
- iOS响应链和传递机制
iOS中加载的时候会先执行main函数 int main(int argc, charchar * argv[]) { @autoreleasepool { return UIApplicationM ...
- JS异步上传文件
直接调用Upload(option)方法,即可上传文件,不需要额外的插件辅助,采用原生js编写. /* *异步上传文件 *option参数 **url:上传路径 **data:上传的其他数据{id:& ...
- Linux系统下C语言获取Time
获取时间的函数有很多,具体包括如下: time()/gettimeofday()等等,下面是获取具体到usecond的时间程序: #include <iostream> #include ...
- Beacon
1.Beacon技术指的是通过使用低功耗蓝牙技术(Bluetooth Low Energy,也就是Bluetooth 4.0或者Bluetooth Smart),Beacon基站便可以自动创建一个信号 ...
- cmd 创建并写入文件
一.建立空文件的几种方法1.cd.>a.txtcd.表示改变当前目录为当前目录,即等于没改变:而且此命令不会有输出.>表示把命令输出写入到文件.后面跟着a.txt,就表示写入到a.txt. ...
- kotlin面向对象之接口、代理与委托、单例模式
接口: 对于什么是接口这里就不概述了,跟java中的概念一样,下面直接上代码进行操练: 而男人跟女人的接口当然也是不同的,很显然男人跟女人最大的差别就是拥有"小弟弟"[我黄我暴利] ...
- 高性能mysql 第11章 可扩展的mysql
可扩展性的定义:当增加资源以获得执行更多的工作系统能获得划算的同等提升. 向上扩展(垂直扩展):提升服务器的硬件性能. 向外扩展(水平扩展):一般都是复制,拆分,数据分片(sharding). 复制: ...
- Python CGI编程Ⅳ
使用POST方法传递数据 使用POST方法向服务器传递数据是更安全可靠的,像一些敏感信息如用户密码等需要使用POST传输数据. 以下同样是hello_get.py ,它也可以处理浏览器提交的POST表 ...
- vue 项目, 通知子组件更新,父组件中每次点击按钮重新加载子组件,(重新生成dom 元素)
vue是组件化开发的项目,很多情况下会把公共组件提取出来,来减少代码量,提高开发效率,和以后更好的可维护性.很多情况下,父组件中都会引用子组件这种情况.通过给在父组件中引用的子组件标签上添加属性,来渲 ...