Android事件分发机制浅析(1)
本文来自网易云社区
作者:孙有军
事件机制是Android中一个比较复杂且重要的知识点,比如你想自定义拦截事件,或者某系组件中嵌套了其他布局,往往会出现这样那样的事件冲突,坑爹啊!!事件主要涵盖onTouch,onClick,onTouchEvent,dispatchTouchEvent,onInterceptTouchEvent等等一系列事件,并且事件间还相互交互耦合,甚至有的事件还有返回值,一会true,一会false,什么情况下返回true,什么情况下返回false,为什么要有返回值,想想这些就感觉整个人都不好了。
但是(万恶的但是),该知识点还是必须要掌握的,知识的深度与广度决定了你走的远度,鉴于此我们就来捅一捅该知识点。
准备工作
俗话说工欲善其事必先利其器,为了看他的执行流程,我们还是先写个样例,打几个日志看看执行流程吧!
首先自定义一个外层布局的Layout,自定义Layout继承了LinearLayout,复写了相应的函数,在调用之前输入日志。如下:
public class Layout extends LinearLayout {
public Layout(Context context) {
super(context);
init();
} public Layout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
} public Layout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} private void init() {
//requestDisallowInterceptTouchEvent(false);
} @TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("Event", "Layout onInterceptTouchEvent " + MotionEvent.actionToString(ev.getAction()));
return super.onInterceptTouchEvent(ev);
} @TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("Event", "Layout onTouchEvent " + MotionEvent.actionToString(event.getAction()));
return super.onTouchEvent(event);
} @TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("Event", "Layout dispatchTouchEvent " + MotionEvent.actionToString(event.getAction()));
return super.dispatchTouchEvent(event);
} }
我们还自定义了一个LogTextView,继承自TextView,也是为了输出日志,代码如下:
public class LogTextView extends TextView { public LogTextView(Context context) {
super(context);
} public LogTextView(Context context, AttributeSet attrs) {
super(context, attrs);
} public LogTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} @TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("Event", "TextView onTouchEvent " + MotionEvent.actionToString(event.getAction()));
return super.onTouchEvent(event);
} @Override
public void setOnTouchListener(OnTouchListener l) {
super.setOnTouchListener(l);
} @Override
public void setOnClickListener(OnClickListener l) {
super.setOnClickListener(l);
}
}
接下来是布局文件了:
<?xml version="1.0" encoding="utf-8"?>
<com.sunny.event.wigdet.Layout
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:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"> <com.sunny.event.wigdet.LogTextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="40dp"
android:background="#999999"
android:padding="20dp"
android:text="Hello World!"/> <ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:src="@mipmap/ic_launcher"/>
</com.sunny.event.wigdet.Layout>
布局中嵌套了两个view,一个TextView,一个ImageView。最后就是主界面了。
public class MainActivity extends AppCompatActivity { private LogTextView tv;
private ImageView imageView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViews();
setViewListener();
} private void findViews() {
tv = (LogTextView) findViewById(R.id.textView);
imageView = (ImageView) findViewById(R.id.image);
} private void setViewListener() {
tv.setOnTouchListener(new View.OnTouchListener() {
@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("Event", "TextView onTouch " + MotionEvent.actionToString(event.getAction()));
return true;
}
});
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("Event", "TextView onClick ");
}
});
imageView.setOnTouchListener(new View.OnTouchListener() {
@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("Event", "ImageView onTouch " + MotionEvent.actionToString(event.getAction()));
return false;
}
});
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("Event", "ImageView onClick ");
}
});
}
}
执行结果
round 1
TextView的onTouch返回为false,点击TextView,日志如下:
05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: Layout dispatchTouchEvent ACTION_DOWN
05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: Layout onInterceptTouchEvent ACTION_DOWN
05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: TextView onTouch ACTION_DOWN
05-05 14:04:44.091 27644-27644/com.sunny.event E/Event: TextView onTouchEvent ACTION_DOWN
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: Layout dispatchTouchEvent ACTION_MOVE
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: Layout onInterceptTouchEvent ACTION_MOVE
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: TextView onTouch ACTION_MOVE
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: TextView onTouchEvent ACTION_MOVE
05-05 14:04:44.158 27644-27644/com.sunny.event E/Event: Layout dispatchTouchEvent ACTION_UP
05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: Layout onInterceptTouchEvent ACTION_UP
05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: TextView onTouch ACTION_UP
05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: TextView onTouchEvent ACTION_UP
05-05 14:04:44.159 27644-27644/com.sunny.event E/Event: TextView onClick
根据日志我们可以看到首先有一个ACTION_DOWN事件,执行的顺序是Layout的dispatchTouchEvent→onInterceptTouchEvent→(TextView)onTouch要→onTouchEvent,之后的我帕金森发生了,产生了ACTION_MOVE事件,传递的顺序与Down是一致的,最后一个事件是UP事件,正常点击不滑动是不会产生MOVE事件的,在这个这个三个事件最后调用了TextView的onClick事件。
小结:
事件的传递顺序是先外层容器,之后再是具体的View。
onTouch事件先于onTouchEvent事件,onTouchEvent先于onClick事件
round 2
我们将TextView的onTouch事件返回true。重新执行。执行顺序如下:
05-05 14:10:20.556 27644-27644/com.sunny.event E/Event: Layout dispatchTouchEvent ACTION_DOWN
05-05 14:10:20.556 27644-27644/com.sunny.event E/Event: Layout onInterceptTouchEvent ACTION_DOWN
05-05 14:10:20.556 27644-27644/com.sunny.event E/Event: TextView onTouch ACTION_DOWN
05-05 14:10:20.616 27644-27644/com.sunny.event E/Event: Layout dispatchTouchEvent ACTION_MOVE
05-05 14:10:20.616 27644-27644/com.sunny.event E/Event: Layout onInterceptTouchEvent ACTION_MOVE
05-05 14:10:20.616 27644-27644/com.sunny.event E/Event: TextView onTouch ACTION_MOVE
05-05 14:10:20.622 27644-27644/com.sunny.event E/Event: Layout dispatchTouchEvent ACTION_UP
05-05 14:10:20.622 27644-27644/com.sunny.event E/Event: Layout onInterceptTouchEvent ACTION_UP
05-05 14:10:20.622 27644-27644/com.sunny.event E/Event: TextView onTouch ACTION_UP
从日志可以看出如果onTouch返回为true,执行顺序变成了如下:
首先还是ACTION_DOWN事件(Layout)dispatchTouchEvent→onInterceptTouchEvent→(TextView)onTouch,ACTION_MOVE与ACTION_UP执行顺序同ACTION_DOWN,可以发现的是TextView的onTouchEvent事件没有了,并且onClick事件也没有了。
小结
1、onTouch事件的返回值为true会拦截onTouchEvent事件
2、onTouchEvent与onClick有关联
上面的两次执行中每次都调用了onInterceptTouchEvent事件,这个到底又是啥?我们去看看他的返回值是什么?
网易云免费体验馆,0成本体验20+款云产品!
更多网易研发、产品、运营经验分享请访问网易云社区
相关文章:
【推荐】 HTTP/2部署使用
Android事件分发机制浅析(1)的更多相关文章
- Android事件分发机制浅析(3)
本文来自网易云社区 作者:孙有军 我们只看最重要的部分 1: 事件为ACTION_DOWN时,执行了cancelAndClearTouchTargets函数,该函数主要清除上一次点击传递的路径,之后执 ...
- Android事件分发机制浅析(2)
本文来自网易云社区 作者:孙有军 上面的两次执行中每次都调用了onInterceptTouchEvent事件,这个到底又是啥?我们去看看他的返回值是什么? public boolean onInter ...
- Android进阶——Android事件分发机制之dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
Android事件分发机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦 Android事件分发机制的发生在Vi ...
- Android事件分发机制(下)
这篇文章继续讨论Android事件分发机制,首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子 ...
- Android事件分发机制(上)
Android事件分发机制这个问题不止一个人问过我,每次我的回答都显得模拟两可,是因为自己一直对这个没有很好的理解,趁现在比较闲对这个做一点总结 举个例子: 你当前有一个非常简单的项目,只有一个Act ...
- android事件分发机制
android事件分发机制,给控件设置ontouch监听事件,当ontouch返回true时,他就不会走onTouchEvent方法,要想走onTouchEvent方法只需要返回ontouch返回fa ...
- [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...
- Android事件分发机制源码分析
Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...
- 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...
随机推荐
- webpack究竟是什么
在很久很久以前,当我们写一个web网页的时候,js实现的逻辑相对是比较弱的.但随着前端技术的发展,前端能实现的内容越来越多.在js里面加了非常非常多的逻辑,于是呢,我们就发现我们通过这种面向过程的方式 ...
- Educational Codeforces Round 55 (Rated for Div. 2) B. Vova and Trophies 【贪心 】
传送门:http://codeforces.com/contest/1082/problem/B B. Vova and Trophies time limit per test 2 seconds ...
- SSH框架——(二)四层结构:DAO,Service,Controller,View层
1. DAO层: 主要任务:做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此. DAO层的设计:首先是设计DAO层的接口,然后再Spring的配置文件中定义此接口的实现类,然后就可以在模块 ...
- EF Core 2.0中Transaction事务会对DbContext底层创建和关闭数据库连接的行为有所影响
数据库 我们先在SQL Server数据库中建立一个Book表: CREATE TABLE [dbo].[Book]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Cr ...
- Hbuilder连接安卓模拟器,调试app
本人用的是夜神模拟器,所以下面的命令也是基于夜神的,其他模拟器请自行百度. 1.首先,启动HBuilder和夜神模拟器 然后打开cmd命令提示符 cd进入夜神模拟器bin目录 执行以下命令 nox_a ...
- BufPay.com 个人收款接口 接入步骤
作为独立开发者产品需要收款是非常麻烦的,注册公司维护成本太高,市面上各种收款工具要么手续费太高,要么到账很慢,体验很不好. 看到 「BufPay.com 个人收款」 这个收款工具,挺有意思的.原理是监 ...
- 多线程异步非阻塞之CompletionService
引自:https://www.cnblogs.com/swiftma/p/6691235.html 上节,我们提到,在异步任务程序中,一种常见的场景是,主线程提交多个异步任务,然后希望有任务完成就处理 ...
- linux系统环境下的静态库和动态库的制作
linux系统下的应用编程需要系统提供的库文件,包括静态库或动态库.不管是静态库还是动态库,都是编译好的二进制文件.在我们编译程序时要链接的目标文件,静态库是链接的时候直接编译到程序里,和程序成为一体 ...
- Linux中Elasticsearch集群部署
1.下载安装包elasticsearch-6.3.1 安装包自己下载,网上很多 2.安装位置在cd /usr/local/elasticsearch/目录下 3.因为ES使用root权限运行会报错, ...
- curl下载文件
* curl下载文件* 根据业务需求* 通过不同站点去访问路径* 下载文件* 但是不同站点需要设置header头* 这里使用curl方式下载* 具体看代码: //下载地址 $url = 'https: ...