Android 事件机制深入探讨(一)
Android 事件机制深入探讨(二)
阅读本文前, 请先阅读上两篇文章, 本文是以上的扩展深入讲解, 老司机请忽略
上篇文章我们讲解了设置 setOnTouchListener 方法后对事件分发流程的影响, 这篇我们再引入 setOnClickListener 方法, 来加深对事件分发的理解, 老样子, 代码复原, 把 ViewGroup 和 View 的 onTouch 的返回值改为 false, 且为 ViewGroup 和 View 设置点击事件 setOnClickListener 并且打印日志, Activity 完整代码如下:
- public class TouchEventActivity extends AppCompatActivity {
- private CustomLinearLayout mCustomLinearLayout;
- private CustomTextView mCustomTextView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_touch_event);
- mCustomLinearLayout = (CustomLinearLayout) findViewById(R.id.ll_touch_layout);
- mCustomTextView = (CustomTextView) findViewById(R.id.tv_touch_content);
- mCustomLinearLayout.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 ViewGroup 的 --->onTouch");
- return false;
- }
- });
- mCustomTextView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 View 的 --->onTouch");
- return false;
- }
- });
- mCustomLinearLayout.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 ViewGroup 的 --->onClick");
- }
- });
- mCustomTextView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 View 的 --->onClick");
- }
- });
- }
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- Log.e(getClass().getSimpleName(), "这是 Activity 的 --->dispatchTouchEvent");
- return super.dispatchTouchEvent(event);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- Log.e(getClass().getSimpleName(), "这是 Activity 的 --->onTouchEvent");
- return super.onTouchEvent(event);
- }
- }
运行, 点击, 看一下日志:
从日志我们可以看出三个事件都是传递到 View 的 onTouchEvent 事件而终止, 但是 View 的 onTouchEvent 的代码我们并没有改:
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- Log.e(getClass().getSimpleName(), "这是 View 的 --->onTouchEvent");
- return super.onTouchEvent(event);
- }
我们知道 CustomTextView 的 onTouchEvent 方法返回的默认值是 false, 这是为什么呢? 细心的同学发现了, View(CustomTextView)的 onClick 被执行了, 在 ACTION_UP 事件传递结束后, 原因就是我们给 View 设置了 setOnClickListener 方法 我们先来看看 View 的 setOnClickListener 的方法做了什么事, 源码如下:
- public void setOnClickListener(@Nullable OnClickListener l) {
- if (!isClickable()) {
- setClickable(true);
- }
- getListenerInfo().mOnClickListener = l;
- }
上面的代码我们看到 View 不仅保存了 OnClickListener 对象, 而且把标识 clickable 设为 true, 然后我们再看系统 View 的 onTouchEvent 的源码 (这里我不贴出来了), 简单说一下就好, 系统会在 onTouchEvent 判断当前 View 是否可点击, 也就是 clickable 标识, 如果为 true, 返回值就会变成 true, 并且当前事件为 ACTION_UP 的时候, 会去执行 OnClickListener 方法, 这跟我们说的 onTouchEvent 默认的返回值为 false 是矛盾的, 其实之前说的 onTouchEvent 默认的返回值为 false 只是为了方便大家容易理解, 现在更正一下, onTouchEvent 默认的返回值并不一定为 false, 跟当前的 View 有关系, 比如 TextView 是 false,Button(可点击的, clickable 为 true) 是 true, 这样, 我们做一个测试, 自定义一个 Button, 放在 ViewGroup 里面, 且该 Button 不设置 setOnTouchListener 和 OnClickListener,Button 的源码如下:
- public class CustomButton extends Button {
- ...
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- Log.e(getClass().getSimpleName(), "这是 View(Button)的 --->dispatchTouchEvent");
- return super.dispatchTouchEvent(event);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- Log.e(getClass().getSimpleName(), "这是 View(Button)的 --->onTouchEvent");
- return super.onTouchEvent(event);
- }
- }
运行, 点击, 打印日志:
是不是跟 CustomTextView 不一样了, Button 默认是消费 onTouchEvent 事件的, 而 TextView 并没有
还是使用 CustomTextView, 我们把 CustomTextView 的 onTouch 返回值改为 true, 其余不变, 照样设置 OnClickListener 点击事件:
- mCustomTextView.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- Log.e(TouchEventActivity.this.getClass().getSimpleName(), "这是 View 的 --->onTouch");
- return true;
- }
- });
运行, 点击, 查看日志:
从上面的日志我们可以看出, 事件传递到 onTouch 就结束了, onClick 并没有被执行, 原因刚才说过了, onClick 是在 onTouchEvent 的 action 为 ACTION_UP 的时候执行的, 因为我们设置了 setOnTouchListener 并返回了 true, 系统并不会执行 onTouchEvent 方法, 所以 onClick 不会被执行读者要记住这一点, onTouch 的返回值会影响 onClick 方法的执行
来源: https://juejin.im/entry/5a9680636fb9a063501546d9