Android查缺补漏(View篇)--事件分发机制
事件分发机制是Android中非常重要的一个知识点,同时也是难点,相信到目前为止很多Android开发者对事件分发机制并没有一个非常系统的认识,当然也包括博主个人在内。可能在平时的开发工作中我们并没有意识到事件分发机制起到的作用,其实它是时刻存在的只是我们不知道而已,就像一些滑动冲突、点击事件之间的冲突等等大多是因为事件分发处理不当导致的。想起了博主大学时做过一个小项目,里面就出现了滑动冲突的问题,虽然最后在网上一步步看着别人的教程也糊里糊涂的解决了,但终究不知其所以然,那么今天就让我们一起来深入的探索一下事件分发机制吧。
什么是事件分发机制?
说了半天的事件分发机制那到底是个啥东西呢?我们不要把它想象的那么高深莫测,不要在心理上给自己设上障碍,其实很容易理解,博主的理解是:简单来说,事件分发机制就是Android系统对事件传递过程规定的一种事件传递规则,事件都会按照这个规则进行分发传递。
在研究事件分发机制之前,我们先来确定一下分析的步骤,化整为零,各个击破:
- 弄清楚分析目标:MotionEvent。
- 了解三个方法:dispatchTouchEvent(MotionEvent event)、onInterceptTouchEvent(Motion event)、onTouchEvent(MotionEvent event)。
- MotionEvent事件的传递过程
- 小结
MotionEvent
其实点击事件的分发过程就是对MotionEvent事件的分发过程,当用户点击操作按下后,MotionEvent事件随即产生并通过一定的规则传递到指定的View上,这个传递的过程和规则就是事件分发机制。
而点击操作触发MotionEvent事件是一个事件流或者说是一个事件序列,其典型的事件类型有如下三种:
- MotionEvent.ACTION_DOWN:手指刚点下屏幕时触发此类型。
- MotionEvent.ACTION_MOVE:手指在屏幕上移动时会多次触发此类型。
- MotionEvent.ACTION_UP:手指在屏幕上抬起时触发此类型。
要特别注意的是,通常情况下一个MotionEvent事件序列包含一个 ACTION_DOWN 若干个 ACTION_MOVE 和 ACTION_UP 是一个完整的事件序列。(点下去立马抬起手指时,会只有 ACTION_DOWN 和 ACTION_UP,这也是一个完整的事件序列)
dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
- boolean dispatchTouchEvent (MotionEvent event):
分发事件,只要事件能传递到当前View就一定会调用此方法,其返回值是一个布尔类型表示是否消耗事件。返回true代表消耗事件,事件流的后续部分还会接着传递过来;返回false代表不消耗事件,事件流的后续部分就不再传递于此。
- boolean onInterceptTouchEvent (MotionEvent ev):
此方法表示是否拦截MotionEvent事件,只有ViewGroup类型的控件才有此方法。如果此方法返回true表示拦截事件,事件将传递给当前View的onTouchEvent()方法,而不再向其下级的View传递。如果此方法返回false表示不拦截事件,事件将传递给下级View的dispatchTouchEvent()。
- boolean onTouchEvent (MotionEvent event):
此方法用来处理MotionEvent,返回值表示是否消耗事件。返回true表示消耗事件,那么事件流的后续部分还会传递过来;返回false表示不消耗事件,事件将交给上级View的onTouchEvent()处理,如果上级View的onTouchEvent()仍然返回false,那么事件将再交给上级的上级处理,以此类推,如果各级View的onTouchEvent()都不消耗事件,那么事件最终将交给Activity的onTouchEvent()处理。
上文说了这么多还是不够具体,先用流程图大体说明一个以上三个方法的关系,及调用流程,下文还会结合具体示例详细说明在事件分发传递中各个方法的调用规则。
三者关系大体如下图:
MotionEvent事件传递过程
当手指点击屏幕产生一个Touch事件后,事件按照Activity->Window->View的顺序依次传递。
首先会传递给Activity的dispatchTouchEvent(),在此方法内部会将由Window处理,接着事件会传递给根View,根View接收到事件后就会按照事件分发机制去处理事件。
根View在这里就是一个ViewGroup,它在接受到事件后会调用dispatchTouchEvent(),在此方法内部会通过onInterceptTouchEvent()方法判断是否拦截事件,如果onInterceptTouchEvent()返回true就表示它要拦截事件,事件将传递给当前ViewGroup的onTouchEvent()。如果onInterceptTouchEvent()放回false就表示它不拦截事件,事件将传给其下级的View,调用下级View的dispatchTouchEvent()。
根View的下级View可能又是一个ViewGroup,如果这样的话其传递流程同根View一样。无论根View的下级View是不是ViewGroup,如果不拦截事件,最终事件会传递到一个纯View的控件上。
当一个View(纯View控件)接收到事件后,也会调用其dispatchTouchEvent(),然后在此方法内部会调用当前View的onTouchEvent(),如果onTouchEvent()返回true则表示要处理此事件。如果返回false表示不消耗事件,其上级View的onTouchEvent()将被调用,则事件流的后续部分不再传递到当前View,在一个事件流中也不会再调用当前View的dispatchTouchEvent()。
接下来通过具体示例来查看事件传递的流程:
示例一,默认情况下的事件传递流程
创建3个类,一个Activity、一个继承自LinearLayout的View,一个继承自Button的View,并重写他们的dispatchTouchEvent()、onIntercepteTouchEvent()、onTouchEvent(),三个类及布局文件的代码如下:
- EventDispatchActivity
/**
* 事件分发机制测试Activity
* Created by liuwei on 18/1/5.
*/
public class EventDispatchActivity extends AppCompatActivity {
private final static String TAG = "Activity";//EventDispatchActivity.class.getSimpleName();
private EventDispatchTestView edtv_test;
private EventDispatchLinearLayout edll_test;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_dispatch);
edtv_test = ViewUtils.find(this, R.id.edtv_test);
edll_test = ViewUtils.find(this, R.id.edll_test);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// 被调用时输出log,event.getAction表示事件的类型,0:ACTION_DOWN,1:ACTION_UP,2:ACTION_MOVE。
Log.i(TAG, "dispatchTouchEvent: " + event.getAction() + " | 分发事件");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + true);
return super.onTouchEvent(event);
}
}
- EventDispatchLinearLayout
/**
* 事件分发机制测试 ViewGroup
* Created by liuwei on 18/1/5.
*/
public class EventDispatchLinearLayout extends LinearLayout {
private final static String TAG = "——Layout";//EventDispatchLinearLayout.class.getSimpleName();
public EventDispatchLinearLayout(Context context) {
super(context);
}
public EventDispatchLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i(TAG, "dispatchTouchEvent: " + event.getAction() + " | 分发事件");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
Log.i(TAG, "onInterceptTouchEvent: " + event.getAction() + " | 是否拦截:" + false);
return super.onInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + false);
return super.onTouchEvent(event);
}
}
- EventDispatchTestView
/**
* 事件分发机制测试 View
* Created by liuwei on 18/1/5.
*/
public class EventDispatchTestView extends Button {
private final static String TAG = "————View";//EventDistpatchTestView.class.getSimpleName();
public EventDispatchTestView(Context context) {
super(context);
}
public EventDispatchTestView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i(TAG, "dispatchTouchEvent: " + event.getAction() + " | 分发事件");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + true);
return super.onTouchEvent(event);
}
}
- 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="cn.codingblock.view.event_dispatch.EventDispatchActivity">
<cn.codingblock.view.event_dispatch.EventDispatchLinearLayout
android:id="@+id/edll_test"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#cccccc">
<cn.codingblock.view.event_dispatch.EventDispatchTestView
android:id="@+id/edtv_test"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_margin="10dp"
android:background="#000000"/>
</cn.codingblock.view.event_dispatch.EventDispatchLinearLayout>
</LinearLayout>
运行代码,点击EventDispatchTestView(黑色区域),log输出如下(log中的数字表示事件的类型,0:ACTION_DOWN,1:ACTION_UP,2:ACTION_MOVE):
01-05 16:58:05.574 23295-23295/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-05 16:58:05.574 23295-23295/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-05 16:58:05.574 23295-23295/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-05 16:58:05.574 23295-23295/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-05 16:58:05.574 23295-23295/cn.codingblock.view I/————View: onTouchEvent: 0 | 是否消耗事件:true
01-05 16:58:05.611 23295-23295/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-05 16:58:05.611 23295-23295/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-05 16:58:05.611 23295-23295/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 2 | 是否拦截:false
01-05 16:58:05.611 23295-23295/cn.codingblock.view I/————View: dispatchTouchEvent: 2 | 分发事件
01-05 16:58:05.611 23295-23295/cn.codingblock.view I/————View: onTouchEvent: 2 | 是否消耗事件:true
01-05 16:58:05.619 23295-23295/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-05 16:58:05.619 23295-23295/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-05 16:58:05.619 23295-23295/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 1 | 是否拦截:false
01-05 16:58:05.620 23295-23295/cn.codingblock.view I/————View: dispatchTouchEvent: 1 | 分发事件
01-05 16:58:05.620 23295-23295/cn.codingblock.view I/————View: onTouchEvent: 1 | 是否消耗事件:true
由log可以看出ViewGroup的onInterceptTouchEvent方法默认是不拦截事件的,View的onTouchEvent方法默认消耗事件。事件流的ACTION_DOWN类型Motion
Event率先到达View的onTouchEvent方法中,此时onTouchEvent方法返回true,表示要处理事件,所以事件流的后续部分依然经过log中的流程到达了View的onTouchEvent方法中。
示例二、在示例一的基础上,让View的onTouchEvent不消耗事件时的传递流程
接下来让上面的EventDispatchTestView的onTouchEvent返回false:
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + false);
return false;//super.onTouchEvent(event);
}
测试log如下:
01-05 18:18:52.545 10771-10771/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-05 18:18:52.545 10771-10771/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-05 18:18:52.546 10771-10771/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-05 18:18:52.546 10771-10771/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-05 18:18:52.546 10771-10771/cn.codingblock.view I/————View: onTouchEvent: 0 | 是否消耗事件:false
01-05 18:18:52.546 10771-10771/cn.codingblock.view I/——Layout: onTouchEvent: 0 | 是否消耗事件:false
01-05 18:18:52.547 10771-10771/cn.codingblock.view I/Activity: onTouchEvent: 0 | 是否消耗事件:true
01-05 18:18:52.629 10771-10771/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-05 18:18:52.629 10771-10771/cn.codingblock.view I/Activity: onTouchEvent: 2 | 是否消耗事件:true
01-05 18:18:52.630 10771-10771/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-05 18:18:52.630 10771-10771/cn.codingblock.view I/Activity: onTouchEvent: 1 | 是否消耗事件:true
当View的onTouchEvent不消耗事件时,事件会交给ViewGroup的onTouchEvent方法处理,而从log可以看出ViewGroup的onTouchEvent默认也不消耗事件,所以事件由交给Activity的onTouchEvent方法处理,最终事件流的后续部分不再传递给ViewGroup和View,而是直接传递给Activity的onTouchEvent处理。
示例三、在示例二的基础上让ViewGroup消耗事件
修改EventDispatchLinearLayout的onTouchEvent(),让其返回true。
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + true);
return true;//super.onTouchEvent(event);
}
测试log如下:
01-05 18:34:53.409 21169-21169/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-05 18:34:53.409 21169-21169/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-05 18:34:53.409 21169-21169/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-05 18:34:53.409 21169-21169/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-05 18:34:53.410 21169-21169/cn.codingblock.view I/————View: onTouchEvent: 0 | 是否消耗事件:false
01-05 18:34:53.410 21169-21169/cn.codingblock.view I/——Layout: onTouchEvent: 0 | 是否消耗事件:true
01-05 18:34:53.420 21169-21169/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-05 18:34:53.420 21169-21169/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-05 18:34:53.420 21169-21169/cn.codingblock.view I/——Layout: onTouchEvent: 2 | 是否消耗事件:true
01-05 18:34:53.470 21169-21169/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-05 18:34:53.470 21169-21169/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-05 18:34:53.470 21169-21169/cn.codingblock.view I/——Layout: onTouchEvent: 1 | 是否消耗事件:true
此种情况下,事件流的ACTION_DOWN先到达View的onTouchEvent,发现它不消耗事件,继而返回上级的ViewGroup的onTouchEvent中,发现它要消耗事件,事件流的后续部分就不在传递给View,也不在调用ViewGroup的onInterceptTouchEvent方法,因为已经知道View不处理事件,所以没必要再通过onInterceptTouchEvent方法来判断了。
示例四、如果在ViewGroup的onInterceptTouchEvent中返回了true拦截了事件,整个事件将不再传递给View而是直接交由ViewGroup的onTouchEvent处理。
修改EventDispatchLinearLayout的onInterceptTouchEvent(),让其返回true。
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
Log.i(TAG, "onInterceptTouchEvent: " + event.getAction() + " | 是否拦截:" + true);
return true;//super.onInterceptTouchEvent(event);
}
测试log如下:
01-05 19:03:21.788 9733-9733/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-05 19:03:21.789 9733-9733/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-05 19:03:21.789 9733-9733/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:true
01-05 19:03:21.789 9733-9733/cn.codingblock.view I/——Layout: onTouchEvent: 0 | 是否消耗事件:true
01-05 19:03:21.819 9733-9733/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-05 19:03:21.819 9733-9733/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-05 19:03:21.819 9733-9733/cn.codingblock.view I/——Layout: onTouchEvent: 2 | 是否消耗事件:true
01-05 19:03:21.877 9733-9733/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-05 19:03:21.877 9733-9733/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-05 19:03:21.877 9733-9733/cn.codingblock.view I/——Layout: onTouchEvent: 1 | 是否消耗事件:true
示例五、给View绑定OnTouchListener和OnClickListener监听器。
在EventDispatchActivity的onCreate()方法里面添加如下代码,并将EventDispatchLinearLayout和EventDispatchTestView的各方法的返回值都还原成示例一中的状态。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_dispatch);
edtv_test = ViewUtils.find(this, R.id.edtv_test);
edll_test = ViewUtils.find(this, R.id.edll_test);
edtv_test.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// 为了log显示的层次更加清晰,这里的TAG使用View的TAG
Log.i("————View", "onTouch: 返回 " + false);
return false;
}
});
edtv_test.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 为了log显示的层次更加清晰,这里的TAG使用View的TAG
Log.i("————View", "onClick: ");
}
});
}
测试log如下:
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/————View: onTouch: 返回 false
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/————View: onTouchEvent: 0 | 是否消耗事件:true
01-06 19:35:07.573 6737-6737/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-06 19:35:07.573 6737-6737/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-06 19:35:07.573 6737-6737/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 2 | 是否拦截:false
01-06 19:35:07.573 6737-6737/cn.codingblock.view I/————View: dispatchTouchEvent: 2 | 分发事件
01-06 19:35:07.574 6737-6737/cn.codingblock.view I/————View: onTouch: 返回 false
01-06 19:35:07.574 6737-6737/cn.codingblock.view I/————View: onTouchEvent: 2 | 是否消耗事件:true
01-06 19:35:07.673 6737-6737/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 1 | 是否拦截:false
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/————View: dispatchTouchEvent: 1 | 分发事件
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/————View: onTouch: 返回 false
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/————View: onTouchEvent: 1 | 是否消耗事件:true
01-06 19:35:07.704 6737-6737/cn.codingblock.view I/————View: onClick:
然后再上面修改代码,让onTouch()方法消耗事件,也就是返回true,再观察log:
edtv_test.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// 为了log显示的层次更加清晰,这里的TAG使用View的TAG
Log.i("————View", "onTouch: 返回 " + false);
return false;
}
});
log如下:
01-07 11:03:55.411 2757-2757/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-07 11:03:55.412 2757-2757/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-07 11:03:55.412 2757-2757/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-07 11:03:55.412 2757-2757/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-07 11:03:55.412 2757-2757/cn.codingblock.view I/————View: onTouch: 返回 true
01-07 11:03:55.542 2757-2757/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-07 11:03:55.542 2757-2757/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-07 11:03:55.542 2757-2757/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 2 | 是否拦截:false
01-07 11:03:55.542 2757-2757/cn.codingblock.view I/————View: dispatchTouchEvent: 2 | 分发事件
01-07 11:03:55.542 2757-2757/cn.codingblock.view I/————View: onTouch: 返回 true
01-07 11:03:55.560 2757-2757/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-07 11:03:55.560 2757-2757/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-07 11:03:55.560 2757-2757/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 1 | 是否拦截:false
01-07 11:03:55.560 2757-2757/cn.codingblock.view I/————View: dispatchTouchEvent: 1 | 分发事件
01-07 11:03:55.560 2757-2757/cn.codingblock.view I/————View: onTouch: 返回 true
从log中我们可以看出:
- 为View绑定的OnTouchListener中的onTouch()方法是优先于View的onTouchEvent()方法执行的。如果在onTouch()消耗了事件(返回true),那么事件将不在传递给onTouchEvent()方法,最终也不会调用onClick()方法。
- 为View绑定的OnClickListener中的onClick()方法优先级最低,是在整个事件流结束后才会被调用,也就是需要经过手指的按下--抬起这个过程才会触发onClick()方法。
小结
为了更好的理解,可以把事件流看成是一队人,把ACTION_DOWN类型看做探路人,探路人按规定的线路先走一遍,直到走到View的onTouchEvent这里,如果onTouchEvent返回true,可理解成此路通,后续部队可以过来。如果返回false,可以理解成此路不通,然后探路人再到Layout(ViewGroup)的onTouchEvent中问路通不通,如果通的话后续部队就不用再去View那里了,直接到ViewGroup这来就可以了。而如果ViewGroup这里路也不通,那么探路人就只能去Activity的onTouchEvent那里了,后续部队也直接去Activity的onTouchEvent这里就可以了。
最后想说的是,本系列文章为博主对Android知识进行再次梳理,查缺补漏的学习过程,一方面是对自己遗忘的东西加以复习重新掌握,另一方面相信在重新学习的过程中定会有巨大的新收获,如果你也有跟我同样的想法,不妨关注我一起学习,互相探讨,共同进步!
参考文献:
- 《Android开发艺术探索》
Android查缺补漏(View篇)--事件分发机制的更多相关文章
- Android查缺补漏(View篇)--事件分发机制源码分析
在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)-- ...
- Android查缺补漏(View篇)--自定义 View 的基本流程
View是Android很重要的一部分,常用的View有Button.TextView.EditView.ListView.GridView.各种layout等等,开发者通过对这些View的各种组合以 ...
- Android查缺补漏(IPC篇)-- Bundle、文件共享、ContentProvider、Messenger四种进程间通讯介绍
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8387752.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- Android查缺补漏(IPC篇)-- 款进程通讯之AIDL详解
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- Android查缺补漏(IPC篇)-- 进程间通讯之AIDL详解
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- Android查缺补漏(IPC篇)-- 进程间通讯基础知识热身
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8479282.html 在Android中进程间通信是比较难的一部分,同时又非常 ...
- Android查缺补漏(IPC篇)-- 进程间通讯之Socket简介及示例
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8425736.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...
- Android查缺补漏(线程篇)-- IntentService的源码浅析
本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8975114.html 在Android中有两个比较容易弄混的概念,Servic ...
- Android View的事件分发机制
准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup ...
- Android View的事件分发机制和滑动冲突解决方案
这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...
随机推荐
- 编译器 STVD 与 IAR 的差别 个人体验
编译器 STVD 与 IAR 的一些差别,这些事个人的体验,欢迎扔砖和指点或者补充. 1:全局查找功能: STVD:全局查找功能全局查找功能比較麻烦.有3个动作. IAR:有全局查找功能比較方便.仅仅 ...
- kotlin web开发教程【一】从零搭建kotlin与spring boot开发环境
IDEA中文输入法的智能提示框不会跟随光标的问题 我用的开发工具是IDEA 这个版本的IDEA有一个问题: 就是中文输入法的智能提示框不会跟随光标 解决这个问题的办法很简单,只有在安装目录下把JRE文 ...
- Hibernate的load()和get()区别
最近在用Hibernate的时候发现一个问题:比如我们从数据库获得一个对象时,使用session.get()方法还是session.load()? 两种方法在获得一个实体对象时是有区别的,在查询性能 ...
- Swagger文档添加file上传参数写法
想在swagger ui的yaml文档里面写一个文件上传的接口,找了半天不知道怎么写,终于搜到了,如下: /tools/upload: post: tags: - "tool" s ...
- ext.net在使用水晶报表时页面无数据显示,并报错误Uncaught ReferenceError: bobj is not defined.
一.错误描述 在公司做项目的时候,有时会需要用到水晶报表显示数据,水晶报表在ASP.NET中使用时没有问题,winform项目开发也没有问题,但是在ext.net开发使用时却报错了,错误:Uncaug ...
- 来腾讯云开发者实验室 学习.NET
腾讯云开发者实验室为开发者提供了一个零门槛的在线实验平台,开发者实验室提供的能力: 零门槛扫码即可免费领取实验机器,支持使用自有机器参与,实验完成后支持保留实验成果: 在线 WEB IDE 支持 sh ...
- button的用法
C# 如何去掉button按钮的边框线? 设置FlatStyle为Flat,并且设置FlatAppearance下的BorderSize为0.
- Python模块之pickle(列表,字典等复杂数据类型与二进制文件的转化)
1.pickle模块简介 The pickle module implements binary protocols for serializing and de-serializing a Pyth ...
- [置顶]
Xamarin android如何调用百度地图入门示例(一)
在Xamarin android如何调用百度地图呢? 首先我们要区分清楚,百度地图这是一个广泛的概念,很多刚刚接触这个名词"百度地图api",的确是泛泛而谈,我们来看一下百度地图的 ...
- Spring之DAO一
前面博客把bean.aop简单了解了一下,今天主要是了解Spring中DAO层,如果使用传统的JDBC时需要创建连接.打开.执行sql.关闭连接这一系列的步骤,Spring框架对JDBC进行了封装,我 ...