Android事件分发机制[-View-] 源码级
向那些曾经无法跨越的鸿沟敬上----吾王已至

开篇先明确几点
1.有三东西挺长的,又长得挺像,看着晃眼且心烦,文中以下简写为:
|-- 分发 dispatchTouchEvent = d16t
|-- 截断 onInterceptTouchEvent = o19t
|-- 消费 onTouchEvent = o10t
2.事件分发机制的参与者与各自拥有的回调方法:
|-- 灰色 Activity: o10t d16t
|-- 紫色 ViewGroup: o10t d16t o19t
|-- 橙色 单体View: o10t d16t
3.MotionEvent的几种常见时间
|-- MotionEvent.ACTION_DOWN = 0; 按下
|-- MotionEvent.ACTION_UP = 1; 抬起
|-- MotionEvent.ACTION_MOVE=2; 移动
|-- MotionEvent.ACTION_CANCEL=3; 取消
图例这里给出来,了解一下
下面表示:触点在Activity上,
按下事件(即0)触发了 d16t(即dispatchTouchEvent)
然后按下事件(即0)触发了 o10t( 即onTouchEvent)
抬起事件(即1)触发了 d16t(即dispatchTouchEvent)
然后抬起事件(即1)触发了 o10t( 即onTouchEvent) ---我想这样应该表述的淋漓尽致了

一、正常情况下的事件传递
0.自定义两个测试View
布局树如下:现在自定义一个单体View,一个ViewGroup

/**
* 作者:张风捷特烈<br/>
* 时间:2019/2/21/021:9:11<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:事件测试Activity
*/
public class EventActivity extends AppCompatActivity {
private static final String TAG = "EventTest";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_test);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "onTouchEvent:--" + event.getAction() + " --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍");
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "dispatchTouchEvent:--" + ev.getAction() + "--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍");
return super.dispatchTouchEvent(ev);
}
}
/**
* 作者:张风捷特烈<br/>
* 时间:2019/2/21/021:9:06<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:事件测试View单体
*/
public class SonView extends View {
private static final String TAG = "EventTest";
public SonView(Context context) {
super(context);
}
public SonView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xffE58F46);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e(TAG, "SonView--dispatchTouchEvent:" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "SonView--onTouchEvent:--" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
return super.onTouchEvent(event);
}
}
/**
* 作者:张风捷特烈<br/>
* 时间:2019/2/21/021:9:06<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:事件测试ViewGroup
*/
public class FatherViewGroup extends FrameLayout {
private static final String TAG = "EventTest";
public FatherViewGroup(Context context) {
super(context);
}
public FatherViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xff9869B7);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "FatherViewGroup--dispatchTouchEvent:--" + ev.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TAG, "FatherViewGroup--onInterceptTouchEvent:--" + ev.getAction() + " -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "FatherViewGroup--onTouchEvent:--" + event.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return super.onTouchEvent(event);
}
}
1.在两View之外点击
图例部分说过了,这里不废话了


2019-02-21 10:13:25.773 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:13:25.775 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:13:25.805 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:13:25.805 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2.点击紫色ViewGroup
在Activity的
d16t
时,之后,事件到了ViewGroup里


2019-02-21 10:36:34.040 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:36:34.040 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:36:34.040 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:36:34.041 : FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:36:34.041 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:36:34.064 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:36:34.064 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
3.点击橙色View单体
在ViewGroup的
o18t
时,之后,事件到了View里,
这里感觉像是...一根链条。万一哪块掉链子了会怎么样?


2019-02-21 10:46:15.721 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:46:15.722 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:46:15.722 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:46:15.722 : SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 10:46:15.723 : SonView--onTouchEvent:--0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 10:46:15.724 : FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 10:46:15.726 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:46:15.772 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 10:46:15.773 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
二、掉链子测试
1.d16t
即dispatchTouchEvent
:
口号:
宁为玉碎不为瓦全,我得不到的,你也别想得到!
1.1.Activity的d16t
掉链子:宁为玉碎不为瓦全
Activity说:
哥不爽了,事件不给你们玩!d16t 返回 false.
说完把event扔了
这链子一断...就算点击View单体,事件也传不下去了。

---->[EventActivity#dispatchTouchEvent]------------------------------
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "dispatchTouchEvent:--" + ev.getAction() + "--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍");
return false;
}
2019-02-21 11:01:19.109 29054-29054/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:01:19.159 29054-29054/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
1.2.ViewGroup的d16t
掉链子
现在回到
初始情况
,Activity把事件给了ViewGroup玩,ViewGroup说:爷也不爽了!
然后一丢
测试中看来:在ViewGroup的d16t
返回false之后会回调Activity的o10T

---->[FatherViewGroup#dispatchTouchEvent]--------------------
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e(TAG, "FatherViewGroup--dispatchTouchEvent:--" + ev.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return false;
}
2019-02-21 11:27:07.487: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:27:07.488: FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:27:07.489: onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:27:07.538: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:27:07.538: onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
1.3.View单体的d16t
掉链子
现在回到
初始情况

---->[SonView#dispatchTouchEvent]--------------------
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e(TAG, "SonView--dispatchTouchEvent:" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
return false;
}
2019-02-21 11:28:32.080: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:28:32.081: FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:28:32.081: FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:28:32.081: SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 11:28:32.082: FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:28:32.083: onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:28:32.121: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:28:32.121: onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
1.4 小结一下d16t
从测试上看到的结论:
dispatchTouchEvent的作用在于分发事件,源头出发不走,后面都白忙活
只要发走了,后面哪里断掉,就会触发上一级的 o10t
2.o18t
对事件的影响
作为ViewGroup独有的方法,onInterceptTouchEvent可以决定事件是否打断

---->[FatherViewGroup#onInterceptTouchEvent]--------------------
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TAG, "FatherViewGroup--onInterceptTouchEvent:--" + ev.getAction() + " -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return true;
}
2019-02-21 11:54:46.648 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:54:46.649 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:54:46.649 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:54:46.650 : FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 11:54:46.652 : onTouchEvent:--0 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:54:46.697 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 11:54:46.697 : onTouchEvent:--1 --卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
3.o10t
掉链子
3.1:当ViewGroup消费事件
默认情况下由最顶层消费事件,这里只有让当ViewGroup消费事件,事件就不会往下传递了

---->[FatherViewGroup#onTouchEvent]--------------------
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "FatherViewGroup--onTouchEvent:--" + event.getAction() + " --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯ ");
return true;
}
2019-02-21 12:09:06.605 11221-11221/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:09:06.606 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:09:06.606 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:09:06.606 11221-11221/com.toly1994.analyzer E/EventTest: SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:09:06.607 11221-11221/com.toly1994.analyzer E/EventTest: SonView--onTouchEvent:--0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:09:06.607 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--onTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:09:06.621 11221-11221/com.toly1994.analyzer E/EventTest: dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:09:06.621 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--dispatchTouchEvent:--1 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:09:06.622 11221-11221/com.toly1994.analyzer E/EventTest: FatherViewGroup--onTouchEvent:--1 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
3.2:当子View消费事件

---->[SonView#onTouchEvent]--------------------
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TAG, "SonView--onTouchEvent:--" + event.getAction() + "-- ††††††††††††††††††††††††††††††††††††††††");
return true;
}
2019-02-21 12:10:05.682 : dispatchTouchEvent:--0--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:10:05.683 : FatherViewGroup--dispatchTouchEvent:--0 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:10:05.683 : FatherViewGroup--onInterceptTouchEvent:--0 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:10:05.683 : SonView--dispatchTouchEvent:0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:10:05.683 : SonView--onTouchEvent:--0-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:10:05.724 : dispatchTouchEvent:--1--卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍卍
2019-02-21 12:10:05.724 : FatherViewGroup--dispatchTouchEvent:--1 --☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:10:05.724 : FatherViewGroup--onInterceptTouchEvent:--1 -- ☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯☯
2019-02-21 12:10:05.724 : SonView--dispatchTouchEvent:1-- ††††††††††††††††††††††††††††††††††††††††
2019-02-21 12:10:05.724 : SonView--onTouchEvent:--1-- ††††††††††††††††††††††††††††††††††††††††
3.3 小结一下o10t
从测试上看到的结论:
onTouchEvent的作用在于消费事件,消费之后不会再往下传递
这里还是想强调一下[d16t]和[o10t]的区别,一者消费,一者分发
由于消费在分发之前,消费是不会阻碍分发的,但分发会影响消费
老婆(Activity):给你100块当做一月生活费,这叫分发dispatchTouchEvent,生活费相当MotionEvent
你(ViewGroup):拿到这100块,可以决定是否把这100块给儿子(View)当生活费(dispatchTouchEvent)
当决定给了,但是中途还可以通过onInterceptTouchEvent打断给的念头...
|--儿子拿到钱花了,钱就没了
|--儿子拿到钱不花,就被老爸没收了,老爸花了,钱就没了
|--儿子拿到钱不花,就被老爸没收了,老爸没花,就被老婆没收了,老婆花了,钱就没了
差不多就是这个理,默认下你和儿子都是不敢花的,但都在手里过了一遍
后面人有没有得花,首先要看老婆给不给,不给,后面就没戏了...
上面如果理清楚,使用方面应该就没问题了
二、源码查看
1.Activity和ViewGroup中的dispatchTouchEvent

---->[Activity#dispatchTouchEvent]----------------------------
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
--->if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
--->return onTouchEvent(ev);
}
|---这里可以很清楚的看清Activity的onTouchEvent回调的时机即
getWindow().superDispatchTouchEvent(ev) 返回false时触发
|---getWindow这里就不废话了,前面都说过,是PhoneWindow对象,直接进
---->[PhoneWindow#dispatchTouchEvent]----------------------------
|--- 调用的是mDecor的方法
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
---->[PhoneWindow$DecorView#superDispatchTouchEvent]----------------------------
|--- 这皮球踢得...mDecor是DecorView对象,继承自FrameLayout,在上一辈便是ViewGroup
|--- 所以这样看来,Activity的dispatchTouchEvent的本质也是ViewGroup触发的
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
---->[ViewGroup#superDispatchTouchEvent]----------------------------
|---这个方法大概100多行
/**
* {@inheritDoc}
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
---> final boolean intercepted;//是否打断
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
---> intercepted = onInterceptTouchEvent(ev);//触发onInterceptTouchEvent,默认false
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
intercepted = true;
}
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
---> if (!canceled && !intercepted) {//未被取消并且未被打断
...
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
...
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
...
//这里接了一下mChildren,mChildren的初始化及添加操作稍后分析
final View[] children = mChildren;
---> for (int i = childrenCount - 1; i >= 0; i--) {//这里开始遍历所有的孩子
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
...
newTouchTarget = getTouchTarget(child);
...
resetCancelNextUpFlag(child);
//dispatchTransformedTouchEvent会触发子View的d16t方法
---> if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.---孩子想要在他的范围内接受触摸
...
---> newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
...
// Dispatch to touch targets. ---- 分发到多个触摸点?...
if (mFirstTouchTarget == null) {//mFirstTouchTarget在addTouchTarget方法中被赋值
---> handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
//下面遍历target的链表,取出链表中的View执行dispatchTransformedTouchEvent
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
---> if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}
....
return handled;
}
---->[ViewGroup#dispatchTransformedTouchEvent]----------------------------
|--总的来说就是触发super或是child的d16t方法,ViewGroup的super是谁?
|--答:View 。child 如果不为空走自己的d16t,如果child还是ViewGroup,就再走一圈上面的
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
...
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {//取消时
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
---> handled = super.dispatchTouchEvent(event);//孩子为空触发super的d16t,
} else {
handled = child.dispatchTouchEvent(event);//孩子不为空,触发d16t
}
event.setAction(oldAction);
return handled;
}
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
---> handled = super.dispatchTouchEvent(event);
} else {
...
---> handled = child.dispatchTouchEvent(event);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
if (child == null) {
---> handled = super.dispatchTouchEvent(transformedEvent);
} else {
...
---> handled = child.dispatchTouchEvent(transformedEvent);
}
transformedEvent.recycle();
return handled;
}
---->[ViewGroup#addTouchTarget]----------------------------
private TouchTarget addTouchTarget(View child, int pointerIdBits) {
TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}
---->[TouchTarget]----------------------------
|---描述触摸的View以及手指的id们
|---TouchTarget是ViewGroup的一个内部类,看样子是一个单链表
|---有一个next的TouchTarget字段,链表承载数据类型是View
private static final class TouchTarget {
...
public View child;
public TouchTarget next;
...
}
---->[ViewGroup$TouchTarget#obtain]----------------------------
|-- 这里很明显是第一个元素出链表
public static TouchTarget obtain(View child, int pointerIdBits) {
final TouchTarget target;
synchronized (sRecycleLock) {
if (sRecycleBin == null) {
target = new TouchTarget();
} else {
target = sRecycleBin;
sRecycleBin = target.next;
sRecycledCount--;
target.next = null;
}
}
target.child = child;
target.pointerIdBits = pointerIdBits;
return target;
}
---->[ViewGroup#onInterceptTouchEvent]---------------------------
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
2.View的dispatchTouchEvent
看这个松了一口气...
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.isTargetAccessibilityFocus()) {
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
//注意,这四个条件满足,直接返回true,就不会触发onTouchEvent了
//并且会触发mOnTouchListener的onTouch回调
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
---> && li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
---> if (!result && onTouchEvent(event)) {//这里触发了View的onTouchEvent!!!,感动...
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
4.关于onInterceptTouchEvent
也许你没注意,刚才已经被消灭了...它只是用来控制的boolean而已
---->[ViewGroup#onInterceptTouchEvent]---------------------------
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
5.关于:onTouchEvent
---->[Activity#onTouchEvent]-----------------------
|--少得可怜,基本上就是false了
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
---->[ViewGroup#onTouchEvent]-----------------------
|--咦,我的onTouchEvent呢?竟然没有!!!!
|--这让我挺意外,也就是ViewGroup完全使用View的onTouchEvent
---->[View#onTouchEvent]-----------------------
|--就这个有点说头...这里追踪一下点击事件
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
if ((viewFlags & ENABLED_MASK) == DISABLED) {//表示不可用
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
return (((viewFlags & CLICKABLE) == CLICKABLE //直接滚回
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
}
if (mTouchDelegate != null) {//有了触摸代理
if (mTouchDelegate.onTouchEvent(event)) {//执行代理人的onTouchEvent
return true;////直接滚回
}
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {//满足上面一堆情况
switch (action) {//我们最熟悉的 switch (action)
case MotionEvent.ACTION_UP://抬起时
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
boolean focusTaken = false;
...
if (prepressed) {//被按下
setPressed(true, x, y);//标true
}
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check--这是一个短点击,所以去掉长按检查
removeLongPressCallback();
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();//执行点击
}
}
}
...
break;
case MotionEvent.ACTION_DOWN:
...
break;
case MotionEvent.ACTION_CANCEL:
...
break;
case MotionEvent.ACTION_MOVE:
...
break;
}
return true;
}
return false;
}
---->[View#performClick]----------------------
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
番外:ViewGroup对子View的添加
---->[ViewGroup对子View的添加]---------------------------
private View[] mChildren;
mChildren = new View[ARRAY_INITIAL_CAPACITY];//默认12
---->[ViewGroup#addInArray]---------------------------
private void addInArray(View child, int index) {
View[] children = mChildren;
final int count = mChildrenCount;
final int size = children.length;
if (index == count) {
if (size == count) {
mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
System.arraycopy(children, 0, mChildren, 0, size);
children = mChildren;
}
---> children[mChildrenCount++] = child;
} else if (index < count) {
if (size == count) {
mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
System.arraycopy(children, 0, mChildren, 0, index);
System.arraycopy(children, index, mChildren, index + 1, count - index);
children = mChildren;
} else {
System.arraycopy(children, index, children, index + 1, count - index);
}
---> children[index] = child;
mChildrenCount++;
if (mLastTouchDownIndex >= index) {
mLastTouchDownIndex++;
}
} else {
throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
}
}
|--- 关于ViewGroup添加View,追踪了一下:
addView(一参)-->addView(两参)-->addView(三参)-->addViewInner-->addInArray
小结:
总的来说源码看下来,感觉view事件分发机制也并不像我想像中的那么难
在自定义View中至多也就是ViewGroup+子View的触摸事件协调,Activity一般不参和
Activity的事件分发实质上是DecorView的事件分发,所以都是View家的,Activity打了波酱油
最后我想强调一下d10t
和o18t
返回false时的不同点,这也是我以前比较迷惑的:见下图


最后用一个小例子来表达一下通过o18t
来控制事件响应行为
如果父View左滑了,那么就不截断子View的滑动(孩子消费)
如果父View右滑了,那么就截断子View的滑动(自己消费)

/**
* 作者:张风捷特烈<br/>
* 时间:2019/2/21/021:9:06<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:事件测试ViewGroup
* 左滑动 ViewGroup 响应
* 右滑动 View单体 响应
*/
public class FatherViewGroup extends FrameLayout {
private static final String TAG = "EventTest";
boolean isLeft = false;
public FatherViewGroup(Context context) {
super(context);
}
public FatherViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xff9869B7);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isLeft;
}
float x;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
x = event.getX();
break;
case MotionEvent.ACTION_MOVE:
float curX = event.getX();
float dx = curX - this.x;
isLeft = dx < 0;
x = curX;
setBackgroundColor(ColUtils.randomColor());
break;
}
return true;
}
}
/**
* 作者:张风捷特烈<br/>
* 时间:2019/2/21/021:9:06<br/>
* 邮箱:1981462002@qq.com<br/>
* 说明:事件测试View单体
*/
public class SonView extends View {
private static final String TAG = "EventTest";
public SonView(Context context) {
super(context);
}
public SonView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(0xffE58F46);
}
float x;
@Override
public boolean onTouchEvent(MotionEvent event) {
setBackgroundColor(ColUtils.randomColor());
return true;
}
}
后记:捷文规范
1.本文成长记录及勘误表
项目源码 | 日期 | 附录 |
---|---|---|
V0.1-- | 2018-2-21 | 无 |
发布名:
Android事件分发机制[源码级]
捷文链接:https://juejin.im/post/5c6e5fd35188252a160f0c51
2.更多关于我
笔名 | 微信 | |
---|---|---|
张风捷特烈 | 1981462002 | zdl1994328 |
我的github:https://github.com/toly1994328
我的简书:https://www.jianshu.com/u/e4e52c116681
我的掘金:https://juejin.im/user/5b42c0656fb9a04fe727eb37
个人网站:http://www.toly1994.com
3.声明
1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持