6 | * >> Attention << 7 | *
8 | * >> Others << 9 | *
10 | * Created by RubiTree ; On 2019-01-14. 11 | */ 12 | 13 | import android.view.MotionEvent; 14 | 15 | /** 16 | * 这里的大逻辑类似 MViewGroup,但细节很多不同,主要因为没有 onIntercept,会简单一些 17 | */ 18 | public class MActivity { 19 | private MViewGroup childGroup; 20 | private boolean isChildNeedEvent = false; 21 | private boolean isSelfNeedEvent = false; 22 | 23 | public MActivity(MViewGroup childGroup) { 24 | this.childGroup = childGroup; 25 | } 26 | 27 | public boolean dispatch(MotionEvent ev) { 28 | boolean handled = false; 29 | 30 | if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 31 | clearStatus(); 32 | 33 | handled = childGroup.dispatch(ev); 34 | if (handled) isChildNeedEvent = true; 35 | 36 | if (!handled) { 37 | handled = onTouch(ev); 38 | if (handled) isSelfNeedEvent = true; 39 | } 40 | } else { 41 | // 这里 isSelfNeedEvent 和 isChildNeedEvent 不会同时为真,顺序无所谓 42 | if (isSelfNeedEvent) { 43 | handled = onTouch(ev); 44 | } else if (isChildNeedEvent) { 45 | handled = childGroup.dispatch(ev); 46 | } 47 | 48 | if (!handled) handled = onTouch(ev); 49 | } 50 | 51 | if (ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { 52 | this.clearStatus(); 53 | } 54 | 55 | return handled; 56 | } 57 | 58 | private void clearStatus() { 59 | isChildNeedEvent = false; 60 | isSelfNeedEvent = false; 61 | } 62 | 63 | public boolean onTouch(MotionEvent ev) { 64 | return false; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubitree/dispatchtoucheventtutorial/sparrowdispatch/sparrow5_java/MView.java: -------------------------------------------------------------------------------- 1 | package com.rubitree.dispatchtoucheventtutorial.sparrowdispatch.sparrow5_java; 2 | 3 | import android.view.MotionEvent; 4 | 5 | /** 6 | * >> Description << 7 | *
8 | * >> Attention << 9 | *
10 | * >> Others << 11 | *
12 | * Created by RubiTree ; On 2019-01-14. 13 | */ 14 | public class MView { 15 | public ViewParent parent = null; 16 | 17 | public boolean dispatch(MotionEvent ev) { 18 | // 源码里没有这么直接但区别不大,主要会考虑是否设置了 onTouchListener 和是否 enable 19 | return onTouch(ev); 20 | } 21 | 22 | public boolean onTouch(MotionEvent ev) { 23 | return false; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubitree/dispatchtoucheventtutorial/sparrowdispatch/sparrow5_java/MViewGroup.java: -------------------------------------------------------------------------------- 1 | package com.rubitree.dispatchtoucheventtutorial.sparrowdispatch.sparrow5_java; 2 | 3 | import android.view.MotionEvent; 4 | import kotlin.jvm.internal.Intrinsics; 5 | 6 | /** 7 | * >> Description << 8 | *
9 | * >> Attention << 10 | *
11 | * >> Others << 12 | *
13 | * Created by RubiTree ; On 2019-01-14. 14 | */ 15 | public class MViewGroup extends MView implements ViewParent { 16 | private MView child; 17 | private boolean isChildNeedEvent = false; 18 | private boolean isSelfNeedEvent = false; 19 | private boolean isDisallowIntercept = false; 20 | 21 | public MViewGroup(MView child) { 22 | this.child = child; 23 | // 这里只是示意,实际中不建议这么写,会造成提前发布未构造完成的实例 24 | child.parent = this; 25 | } 26 | 27 | @Override 28 | public boolean dispatch(MotionEvent ev) { 29 | boolean handled = false; 30 | 31 | if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 32 | clearStatus(); 33 | 34 | if (!isDisallowIntercept && onIntercept(ev)) { 35 | isSelfNeedEvent = true; 36 | handled = onTouch(ev); 37 | }else{ 38 | handled = child.dispatch(ev); 39 | if (handled) isChildNeedEvent = true; 40 | 41 | if (!handled) { // 这里没有用 if else 是因为这样写上下一致,更清晰 42 | handled = onTouch(ev); 43 | if (handled) isSelfNeedEvent = true; // 这一步有没有必要呢?还是有的,会更清晰,否则下面的else要多写一个不太清晰的else 44 | } 45 | } 46 | } else { 47 | // 这里 isSelfNeedEvent 条件判断应该放在 isChildNeedEvent 前面 48 | // 因为两个都为真的情况只能是自己之后通过 onIntercept 抢了控制权,那这之后的控制权就不会去 child 那儿了 49 | if (isSelfNeedEvent) { 50 | handled = onTouch(ev); 51 | } else if (isChildNeedEvent) { 52 | if (!isDisallowIntercept && onIntercept(ev)) { 53 | isSelfNeedEvent = true; 54 | 55 | MotionEvent cancel = MotionEvent.obtain(ev); 56 | cancel.setAction(MotionEvent.ACTION_CANCEL); 57 | handled = child.dispatch(cancel); 58 | cancel.recycle(); 59 | } else { 60 | handled = child.dispatch(ev); 61 | } 62 | } 63 | // 这里不用再 else 了,因为如果 isSelfNeedEvent 和 isChildNeedEvent 都不为 true,上面不会再发事件下来了 64 | } 65 | 66 | if (ev.getActionMasked() == MotionEvent.ACTION_UP || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { 67 | this.clearStatus(); 68 | } 69 | 70 | return handled; 71 | } 72 | 73 | private void clearStatus() { 74 | isChildNeedEvent = false; 75 | isSelfNeedEvent = false; 76 | isDisallowIntercept = false; 77 | } 78 | 79 | public boolean onIntercept(MotionEvent ev) { 80 | return false; 81 | } 82 | 83 | @Override 84 | public boolean onTouch(MotionEvent ev) { 85 | return false; 86 | } 87 | 88 | @Override 89 | public void requestDisallowInterceptTouchEvent(boolean isDisallowIntercept) { 90 | this.isDisallowIntercept = isDisallowIntercept; 91 | if (parent != null) { 92 | parent.requestDisallowInterceptTouchEvent(isDisallowIntercept); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/rubitree/dispatchtoucheventtutorial/sparrowdispatch/sparrow5_java/ViewParent.java: -------------------------------------------------------------------------------- 1 | package com.rubitree.dispatchtoucheventtutorial.sparrowdispatch.sparrow5_java; 2 | 3 | /** 4 | * >> Description << 5 | *
6 | * >> Attention << 7 | *
8 | * >> Others << 9 | *
10 | * Created by RubiTree ; On 2019-01-14.
11 | */
12 | public interface ViewParent {
13 | void requestDisallowInterceptTouchEvent(boolean isDisallowIntercept);
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/rubitree/dispatchtoucheventtutorial/touchdojo/DispatchConfig.kt:
--------------------------------------------------------------------------------
1 | package com.rubitree.dispatchtoucheventtutorial.touchdojo
2 |
3 | import com.rubitree.dispatchtoucheventtutorial.dispatchstrategy.*
4 | import com.rubitree.dispatchtoucheventtutorial.dispatchstrategy.ReturnStrategy.*
5 |
6 | /**
7 | * >> Description <<
8 | *
9 | * >> Attention <<
10 | *
11 | * >> Others <<
12 | * 配置有点多,就没有给每个配置组合命名,简单测试就直接用注释的方式进行配置
13 | *
14 | * Created by RubiTree ; On 2019-01-14.
15 | */
16 | class DispatchConfig {
17 | companion object {
18 |
19 | fun getActivityDispatchDelegate(layer: String = "Activity"): IDispatchDelegate {
20 | return DispatchDelegate(layer)
21 | }
22 |
23 | fun getViewGroupDispatchDelegate(layer: String = "ViewGroup"): IDispatchDelegate {
24 | // return DispatchDelegate(layer)
25 | // return DispatchDelegate(layer, ALL_SUPER, ALL_SUPER, ALL_TRUE)
26 | // return DispatchDelegate(layer, ALL_SUPER, ALL_TRUE, ALL_SUPER)
27 | // return DispatchDelegate(layer, ALL_SUPER, ALL_TRUE, ALL_TRUE)
28 | // return DispatchDelegate(layer, ALL_TRUE, ALL_SUPER, ALL_SUPER)
29 | // return DispatchDelegate(layer, ALL_FALSE, ALL_SUPER, ALL_SUPER)
30 | // return DispatchDelegate(
31 | // layer,
32 | // ALL_SUPER,
33 | // ALL_SUPER,
34 | // EventsReturnStrategy(T_TRUE, EVENT_SUPER, T_SUPER)
35 | // )
36 | // return DispatchDelegate(
37 | // layer,
38 | // ALL_SUPER,
39 | // EventsReturnStrategy(T_TRUE, EVENT_SUPER, T_SUPER),
40 | // EventsReturnStrategy(T_TRUE, EVENT_SUPER, T_SUPER)
41 | // )
42 | return DispatchDelegate(
43 | layer,
44 | ALL_SUPER,
45 | EventsReturnStrategy(T_FALSE, arrayOf(T_FALSE, T_TRUE, T_TRUE), T_SUPER),
46 | ALL_TRUE
47 | )
48 | // return DispatchDelegate(
49 | // layer,
50 | // EventsReturnStrategy(T_TRUE, EVENT_SUPER, T_SUPER),
51 | // ALL_SUPER,
52 | // ALL_SUPER
53 | // )
54 | }
55 |
56 | fun getViewDispatchDelegate(layer: String = "View"): IDispatchDelegate {
57 | // return DispatchDelegate(layer)
58 | return DispatchDelegate(layer, ALL_SUPER, ALL_SUPER, ALL_TRUE)
59 | // return DispatchDelegate(
60 | // layer,
61 | // ALL_SUPER, ALL_SUPER,
62 | // EventsReturnStrategy(T_TRUE, EVENT_SUPER, T_SUPER)
63 | // )
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rubitree/dispatchtoucheventtutorial/touchdojo/SparrowViews.kt:
--------------------------------------------------------------------------------
1 | package com.rubitree.dispatchtoucheventtutorial.touchdojo
2 |
3 | import android.view.MotionEvent
4 | import com.rubitree.dispatchtoucheventtutorial.sparrowdispatch.sparrow5.MActivity
5 | import com.rubitree.dispatchtoucheventtutorial.sparrowdispatch.sparrow5.MView
6 | import com.rubitree.dispatchtoucheventtutorial.sparrowdispatch.sparrow5.MViewGroup
7 | import com.rubitree.dispatchtoucheventtutorial.utils.log
8 | import com.rubitree.dispatchtoucheventtutorial.utils.typeName
9 |
10 | /**
11 | * >> Description <<
12 | *
13 | * >> Attention <<
14 | *
15 | * >> Others <<
16 | *
17 | * Created by RubiTree ; On 2019-01-14.
18 | */
19 | class SparrowView : MView() {
20 | private val dispatchDelegate = DispatchConfig.getViewDispatchDelegate("SView")
21 |
22 | override fun dispatch(ev: MotionEvent): Boolean {
23 | return dispatchDelegate.dispatchTouchEvent(ev) { super.dispatch(ev) }
24 | }
25 |
26 | override fun onTouch(ev: MotionEvent): Boolean {
27 | return dispatchDelegate.onTouchEvent(ev) { super.onTouch(ev) }
28 | }
29 | }
30 |
31 | class SparrowLayout(view: MView) : MViewGroup(view) {
32 | private val dispatchDelegate = DispatchConfig.getViewGroupDispatchDelegate("SViewGroup")
33 |
34 | override fun dispatch(ev: MotionEvent): Boolean {
35 | return dispatchDelegate.dispatchTouchEvent(ev) { super.dispatch(ev) }
36 | }
37 |
38 | override fun onTouch(ev: MotionEvent): Boolean {
39 | return dispatchDelegate.onTouchEvent(ev) { super.onTouch(ev) }
40 | }
41 |
42 | override fun onIntercept(ev: MotionEvent): Boolean {
43 | return dispatchDelegate.onInterceptTouchEvent(ev) { super.onIntercept(ev) }
44 | }
45 | }
46 |
47 | class SparrowActivity(viewGroup: MViewGroup) : MActivity(viewGroup) {
48 | private val dispatchDelegate = DispatchConfig.getActivityDispatchDelegate("SActivity")
49 |
50 | override fun dispatch(ev: MotionEvent): Boolean {
51 | log(" ")
52 | log("[${ev.typeName()}]")
53 | return dispatchDelegate.dispatchTouchEvent(ev) { super.dispatch(ev) }
54 | }
55 |
56 | override fun onTouch(ev: MotionEvent): Boolean {
57 | return dispatchDelegate.onTouchEvent(ev) { super.onTouch(ev) }
58 | }
59 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rubitree/dispatchtoucheventtutorial/touchdojo/SystemViews.kt:
--------------------------------------------------------------------------------
1 | package com.rubitree.dispatchtoucheventtutorial.touchdojo
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.support.v7.app.AppCompatActivity
6 | import android.util.AttributeSet
7 | import android.view.MotionEvent
8 | import android.view.View
9 | import android.widget.FrameLayout
10 | import com.rubitree.dispatchtoucheventtutorial.R
11 | import com.rubitree.dispatchtoucheventtutorial.sparrowdispatch.sparrow5.MActivity
12 | import com.rubitree.dispatchtoucheventtutorial.sparrowdispatch.sparrow5.MView
13 | import com.rubitree.dispatchtoucheventtutorial.sparrowdispatch.sparrow5.MViewGroup
14 | import com.rubitree.dispatchtoucheventtutorial.utils.log
15 | import com.rubitree.dispatchtoucheventtutorial.utils.typeName
16 |
17 | /**
18 | * >> Description <<
19 | *
20 | * >> Attention <<
21 | *
22 | * >> Others <<
23 | *
24 | * Created by RubiTree ; On 2019-01-14.
25 | */
26 | class DelegatedView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
27 | private val dispatchDelegate = DispatchConfig.getViewDispatchDelegate()
28 |
29 | override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
30 | return dispatchDelegate.dispatchTouchEvent(event) { super.dispatchTouchEvent(event) }
31 | }
32 |
33 | override fun onTouchEvent(event: MotionEvent?): Boolean {
34 | return dispatchDelegate.onTouchEvent(event) { super.onTouchEvent(event) }
35 | }
36 | }
37 |
38 | class DelegatedLayout(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
39 | private val dispatchDelegate = DispatchConfig.getViewGroupDispatchDelegate()
40 |
41 | override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
42 | return dispatchDelegate.dispatchTouchEvent(event) { super.dispatchTouchEvent(event) }
43 | }
44 |
45 | override fun onTouchEvent(event: MotionEvent?): Boolean {
46 | return dispatchDelegate.onTouchEvent(event) { super.onTouchEvent(event) }
47 | }
48 |
49 | override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
50 | return dispatchDelegate.onInterceptTouchEvent(ev) { super.onInterceptTouchEvent(ev) }
51 | }
52 | }
53 |
54 | class DelegatedActivity : AppCompatActivity() {
55 |
56 | override fun onCreate(savedInstanceState: Bundle?) {
57 | super.onCreate(savedInstanceState)
58 | setContentView(R.layout.activity_main)
59 | }
60 |
61 | /*--------------------------------------------------------------------------------------------*/
62 |
63 | private val dispatchDelegate = DispatchConfig.getActivityDispatchDelegate()
64 |
65 | override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
66 | log(" ")
67 | log("[${event.typeName()}]")
68 | return dispatchDelegate.dispatchTouchEvent(event) { super.dispatchTouchEvent(event) }
69 | }
70 |
71 | override fun onTouchEvent(event: MotionEvent?): Boolean {
72 | return dispatchDelegate.onTouchEvent(event) { super.onTouchEvent(event) }
73 | }
74 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/rubitree/dispatchtoucheventtutorial/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.rubitree.dispatchtoucheventtutorial.utils
2 |
3 | import android.util.Log
4 | import android.view.MotionEvent
5 |
6 | /**
7 | * >> Description <<
8 | *
9 | * >> Attention <<
10 | *
11 | * >> Others <<
12 | *
13 | * Created by RubiTree ; On 2019-01-14.
14 | */
15 | private const val LOG_TAG = "TouchDojo"
16 |
17 | fun MotionEvent?.log(layer: String, time: String, msg: String = "") {
18 | log("|layer:$layer |on:$time${if (msg.isEmpty()) "" else " |$msg"}")
19 | }
20 |
21 | fun MotionEvent?.log(msg: String) {
22 | Log.d(LOG_TAG, "$msg |type:${typeName()}".replaceFormat())
23 | }
24 |
25 | fun MotionEvent?.typeName(): String {
26 | if (this == null) return "null"
27 |
28 | return when (this.actionMasked) {
29 | MotionEvent.ACTION_DOWN -> "down"
30 | MotionEvent.ACTION_MOVE -> "move"
31 | MotionEvent.ACTION_UP -> "up"
32 | MotionEvent.ACTION_CANCEL -> "cancel"
33 | else -> "?"
34 | }
35 | }
36 |
37 | private fun String.replaceFormat(): String {
38 | var result = this
39 |
40 | result = result.replace("dispatchTouchEvent", "Dispatch")
41 | result = result.replace("onInterceptTouchEvent", "Intercept")
42 | result = result.replace("onTouchEvent", "Touch")
43 |
44 | return result
45 | }
46 |
47 | fun log(msg: String) {
48 | Log.d(LOG_TAG, msg)
49 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 | ## SparrowDispatch
6 |
7 | 可以在此处查看详细内容:[《看穿 > 触摸事件分发》](https://juejin.im/post/5c3c8538f265da6142741d63)
8 |
9 | [SparrowDispatch](https://github.com/RubiTree/DispatchTouchEventTutorial/blob/master/app/src/main/java/com/rubitree/dispatchtoucheventtutorial/sparrowdispatch/sparrow5/SparrowDispatch.kt)把源码中与事件分发相关的内容剥离了出来,至少有这两个优点:
10 |
11 | 1. 相比源码,这份代码足够短足够简单,那些跟事件分发无关的东西通通不要来干扰我
12 | 1. 长度总共不超过150行,剔除了所有跟事件分发无关的代码,并且把一些因为其他细节导致写得比较复杂的逻辑,用更简单直接的方式表达了
13 | 1. 相比那段经典的事件分发伪代码,这份代码又足够详细,详细到能告诉你所有你需要知道的事件分发的具体细节
14 | 1. 那段经典伪代码只能起到提纲挈领的作用,而这份麻雀代码虽然极其精简但它五脏俱全,全到可以直接跑
15 | 1. 你可以用它进行为伪布局,然后触发触摸事件,如果在回调中打印日志,它打印出的事件分发过程与你使用系统控件真实布局时事件分发的过程是一模一样的
16 |
17 | ## 测试
18 |
19 | 测试的思路是通过在每个事件分发的钩子中打印日志来跟踪事件分发的过程。
20 | 于是就需要在不同的 View 层级的不同钩子中,针对不同的触摸事件进行不同的操作,以制造各种事件分发的场景。
21 |
22 | 为了减少重复代码简单搭建了一个测试框架,包括一个可以代理 View 中这些的操作的接口`IDispatchDelegate`及其实现类,和一个`DispatchConfig`统一进行不同的场景的配置。
23 | 之后创建了使用统一配置和代理操作的 真实控件们`SystemViews` 和 用`SparrowDispatch`实现的麻雀控件们`SparrowViews`。
24 |
25 | 在`DispatchConfig`中配置好事件分发的策略后,直接启动`SystemViews`中的`DelegatedActivity`,进行触摸,使用关键字`TouchDojo`过滤,就能得到事件分发的跟踪日志。
26 | 同时,运行`SparrowActivityTest`中的`dispatch()`测试方法,也能得到麻雀控件的事件分发跟踪日志。
27 |
28 | ### 场景一
29 |
30 | 先配置策略,模拟`View`和`ViewGroup`都不消费事件的场景:
31 |
32 | ```kotlin
33 | fun getActivityDispatchDelegate(layer: String = "Activity"): IDispatchDelegate {
34 | return DispatchDelegate(layer)
35 | }
36 |
37 | fun getViewGroupDispatchDelegate(layer: String = "ViewGroup"): IDispatchDelegate {
38 | return DispatchDelegate(layer)
39 | }
40 |
41 | fun getViewDispatchDelegate(layer: String = "View"): IDispatchDelegate {
42 | return DispatchDelegate(layer)
43 | }
44 | ```
45 |
46 | 能看到打印的事件分发跟踪日志:
47 |
48 | ```log
49 | [down]
50 | |layer:SActivity |on:Dispatch_BE |type:down
51 | |layer:SViewGroup |on:Dispatch_BE |type:down
52 | |layer:SViewGroup |on:Intercept_BE |type:down
53 | |layer:SViewGroup |on:Intercept_AF |result(super):false |type:down
54 | |layer:SView |on:Dispatch_BE |type:down
55 | |layer:SView |on:Touch_BE |type:down
56 | |layer:SView |on:Touch_AF |result(super):false |type:down
57 | |layer:SView |on:Dispatch_AF |result(super):false |type:down
58 | |layer:SViewGroup |on:Touch_BE |type:down
59 | |layer:SViewGroup |on:Touch_AF |result(super):false |type:down
60 | |layer:SViewGroup |on:Dispatch_AF |result(super):false |type:down
61 | |layer:SActivity |on:Touch_BE |type:down
62 | |layer:SActivity |on:Touch_AF |result(super):false |type:down
63 | |layer:SActivity |on:Dispatch_AF |result(super):false |type:down
64 |
65 | [move]
66 | |layer:SActivity |on:Dispatch_BE |type:move
67 | |layer:SActivity |on:Touch_BE |type:move
68 | |layer:SActivity |on:Touch_AF |result(super):false |type:move
69 | |layer:SActivity |on:Dispatch_AF |result(super):false |type:move
70 |
71 | [move]
72 | ...
73 |
74 | [up]
75 | |layer:SActivity |on:Dispatch_BE |type:up
76 | |layer:SActivity |on:Touch_BE |type:up
77 | |layer:SActivity |on:Touch_AF |result(super):false |type:up
78 | |layer:SActivity |on:Dispatch_AF |result(super):false |type:up
79 | ```
80 |
81 | 1. 因为系统控件和麻雀控件打印的日志一模一样,所以只贴出一份
82 | 2. 这里用`BE`代表 `before`,表示该方法开始处理事件的时候,用`AF`代表`after`,表示该方法结束处理事件的时候,并且打印处理的结果
83 | 3. 从日志中能清楚看到,当`View`和`ViewGroup`都不消费`DOWN`事件时,后续事件将不再传递给`View`和`ViewGroup`
84 |
85 | ### 场景二
86 |
87 | 再配置策略,模拟`View`和`ViewGroup`都消费事件,同时`ViewGroup`在第二个`MOVE`事件时认为自己需要拦截事件的场景:
88 |
89 | ```kotlin
90 | fun getActivityDispatchDelegate(layer: String = "Activity"): IDispatchDelegate {
91 | return DispatchDelegate(layer)
92 | }
93 |
94 | fun getViewGroupDispatchDelegate(layer: String = "ViewGroup"): IDispatchDelegate {
95 | return DispatchDelegate(
96 | layer,
97 | ALL_SUPER,
98 | // 表示 onInterceptTouchEvent 方法中,DOWN 事件返回 false,第一个 MOVE 事件返回 false,第二个第三个 MOVE 事件返回 true
99 | EventsReturnStrategy(T_FALSE, arrayOf(T_FALSE, T_TRUE, T_TRUE), T_SUPER),
100 | ALL_TRUE
101 | )
102 | }
103 |
104 | fun getViewDispatchDelegate(layer: String = "View"): IDispatchDelegate {
105 | return DispatchDelegate(layer, ALL_SUPER, ALL_SUPER, ALL_TRUE)
106 | }
107 | ```
108 |
109 | 能看到打印的事件分发跟踪日志:
110 |
111 | ```log
112 | [down]
113 | |layer:SActivity |on:Dispatch_BE |type:down
114 | |layer:SViewGroup |on:Dispatch_BE |type:down
115 | |layer:SViewGroup |on:Intercept |result(false):false |type:down
116 | |layer:SView |on:Dispatch_BE |type:down
117 | |layer:SView |on:Touch |result(true):true |type:down
118 | |layer:SView |on:Dispatch_AF |result(super):true |type:down
119 | |layer:SViewGroup |on:Dispatch_AF |result(super):true |type:down
120 | |layer:SActivity |on:Dispatch_AF |result(super):true |type:down
121 |
122 | [move]
123 | |layer:SActivity |on:Dispatch_BE |type:move
124 | |layer:SViewGroup |on:Dispatch_BE |type:move
125 | |layer:SViewGroup |on:Intercept |result(false):false |type:move
126 | |layer:SView |on:Dispatch_BE |type:move
127 | |layer:SView |on:Touch |result(true):true |type:move
128 | |layer:SView |on:Dispatch_AF |result(super):true |type:move
129 | |layer:SViewGroup |on:Dispatch_AF |result(super):true |type:move
130 | |layer:SActivity |on:Dispatch_AF |result(super):true |type:move
131 |
132 | [move]
133 | |layer:SActivity |on:Dispatch_BE |type:move
134 | |layer:SViewGroup |on:Dispatch_BE |type:move
135 | |layer:SViewGroup |on:Intercept |result(true):true |type:move
136 | |layer:SView |on:Dispatch_BE |type:cancel
137 | |layer:SView |on:Touch_BE |type:cancel
138 | |layer:SView |on:Touch_AF |result(super):false |type:cancel
139 | |layer:SView |on:Dispatch_AF |result(super):false |type:cancel
140 | |layer:SViewGroup |on:Dispatch_AF |result(super):false |type:move
141 | |layer:SActivity |on:Touch_BE |type:move
142 | |layer:SActivity |on:Touch_AF |result(super):false |type:move
143 | |layer:SActivity |on:Dispatch_AF |result(super):false |type:move
144 |
145 | [move]
146 | |layer:SActivity |on:Dispatch_BE |type:move
147 | |layer:SViewGroup |on:Dispatch_BE |type:move
148 | |layer:SViewGroup |on:Touch |result(true):true |type:move
149 | |layer:SViewGroup |on:Dispatch_AF |result(super):true |type:move
150 | |layer:SActivity |on:Dispatch_AF |result(super):true |type:move
151 |
152 | [up]
153 | |layer:SActivity |on:Dispatch_BE |type:up
154 | |layer:SViewGroup |on:Dispatch_BE |type:up
155 | |layer:SViewGroup |on:Touch |result(true):true |type:up
156 | |layer:SViewGroup |on:Dispatch_AF |result(super):true |type:up
157 | |layer:SActivity |on:Dispatch_AF |result(super):true |type:up
158 | ```
159 |
160 | 1. 同样因为系统控件和麻雀控件打印的日志一模一样,所以只贴出一份
161 | 2. 从日志中能清楚看到,在`ViewGroup`拦截事件前后,事件是如何分发的
162 |
163 | ## License
164 | Apache License 2.0, here is the [LICENSE](https://github.com/RubiTree/DispatchTouchEventTutorial/blob/master/LICENSE).
165 |
--------------------------------------------------------------------------------
/resource/title.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RubiTree/DispatchTouchEventTutorial/39d9817b6938d3b08e157b8641da8a6ec3526b3d/resource/title.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------