内容简介:布局树如下:现在自定义一个单体View,一个ViewGroup图例部分说过了,这里不废话了在Activity的
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时的不同点,这也是我以前比较迷惑的:见下图
后记:捷文规范
1.本文成长记录及勘误表
项目源码 | 日期 | 附录 |
---|---|---|
V0.1-- | 2018-2-21 | 无 |
发布名: Android事件分发机制[源码级]
捷文链接: juejin.im/post/5c6b71…
2.更多关于我
笔名 | 微信 | |
---|---|---|
张风捷特烈 | 1981462002 | zdl1994328 |
我的github: github.com/toly1994328
我的简书: www.jianshu.com/u/e4e52c116…
我的掘金: juejin.im/user/5b42c0…
个人网站:www.toly1994.com
3.声明
1----本文由张风捷特烈原创,转载请注明
2----欢迎广大编程爱好者共同交流
3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4----看到这里,我在此感谢你的喜欢与支持
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Android事件分发源码归纳
- View的事件分发(二)源码分析
- View的事件分发(三)源码分析
- 基于源码分析 Android View 事件分发机制
- Laravel Queue——消息队列任务与分发源码剖析
- Android读书笔记--从源码角度剖析View事件分发机制
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。