Android 几句话总结事件分发
2021-09-23 本文已影响0人
雁过留声_泪落无痕
结论
- 事件分发遵循从上到下再从下到上的路径进行分发
Activity<=>ViewGroup<=>ViewGroup<=>...<=>ViewGroup<=>View
- Activity ViewGroup View 分别有以下方法
Activity#dispatchTouchEvent(MotionEvent ev)
Activity#onTouchEvent(MotionEvent ev)
ViewGroup#dispatchTouchEvent(MotionEvent ev)
ViewGroup#onInterceptTouchEvent(MotionEvent ev)
ViewGroup#onTouchEvent(MotionEvent ev)
View#dispatchTouchEvent(MotionEvent ev)
View#onTouchEvent(MotionEvent ev)
- 调用方法的路径为,整体呈一个 U 字形
Activity#dispatchTouchEvent(MotionEvent ev) -------- Activity#onTouchEvent(MotionEvent ev)
|| /\
\/ ||
ViewGroup#dispatchTouchEvent(MotionEvent ev)
|| ||
\/ ||
ViewGroup#onInterceptTouchEvent(MotionEvent ev) ---- ViewGroup#onTouchEvent(MotionEvent ev)
|| /\
\/ ||
View#dispatchTouchEvent(MotionEvent ev) ------------ View#onTouchEvent(MotionEvent ev)
|| /\
||===================================================||
- 分发的事件包括
- ACTION_DOWN
- ACTION_MOVE
- ACTION_UP
- ACTION_CANCEL
- 谁处理了 ACTION_DOWN 事件,后续的事件就直接分发到谁
- 依然遵循从上到下,再从下到上,达到目标 View/ViewGroup 时停止
- 若 View 处理了 ACTION_DOWN 事件,后续事件从上到下并到该 View 的:
View#dispatchTouchEvent(MotionEvent ev) => View#onTouchEvent(MotionEvent ev)
- 若 ViewGroup 处理了 ACTION_DOWN 事件,后续事件从上到下并到该 ViewGroup 的(不再走 onInterceptTouchEvent):
ViewGroup#dispatchTouchEvent(MotionEvent ev) => ViewGroup#onTouchEvent(MotionEvent ev)
- 后续事件(ACTION_DOWN 后的第一个 ACTION_MOVE 或者从中间的某个 ACTION_MOVE 开始拦截效果都一样)被上层拦截(某 ViewGroup#onInterceptTouchEvent(MotionEvent ev) 返回 true),则当前事件转换成 ACTION_CANCEL 并传递到本应该(若不拦截的话)处理该事件的 View/ViewGroupdispatchTouchEvent(MotionEvent ev) => View/ViewGroup#onTouchEvent(MotionEvent ev),且后续事件都从上到下并到做了拦截处理的 ViewGroup 的(不论 onTouchEvent 返回值是 true 还是 false,除非再次被更上层的 ViewGroup 拦截):
ViewGroup#dispatchTouchEvent(MotionEvent ev) => ViewGroup#onTouchEvent(MotionEvent ev)
- 无人处理 ACTION_DOWN 事件,则后续事件不再走 ViewGroup 和 View,只到 Activity 的:
Activity#dispatchTouchEvent(MotionEvent ev) => Activity#onTouchEvent(MotionEvent ev)
测试代码
class TestActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val groupA = MyViewGroupA(this)
val groupB = MyViewGroupB(this)
val view = MyView(this)
groupB.addView(view)
groupA.addView(groupB)
setContentView(groupA)
}
}
fun getAction(ev: MotionEvent): String {
var result = ""
when (ev.action) {
MotionEvent.ACTION_DOWN -> result = "down"
MotionEvent.ACTION_MOVE -> result = "move"
MotionEvent.ACTION_UP -> result = "up"
MotionEvent.ACTION_CANCEL -> result = "cancel"
}
return "$result, ${ev.eventTime}"
}
class MyViewGroupA(context: Context) : FrameLayout(context) {
var count = 0
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
Log.d("hehe", "MyViewGroupA#dispatchTouchEvent(), ev: ${getAction(ev)}")
return super.dispatchTouchEvent(ev)
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
Log.d("hehe", "MyViewGroupA#onInterceptTouchEvent(), ev: ${getAction(ev)}")
if (count++ >= 3 && ev.action == MotionEvent.ACTION_MOVE) {
return true
}
return super.onInterceptTouchEvent(ev)
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
Log.d("hehe", "MyViewGroupA#onTouchEvent(), ev: ${getAction(ev)}")
return super.onTouchEvent(ev)
}
}
class MyViewGroupB(context: Context) : FrameLayout(context) {
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
Log.d("hehe", "MyViewGroupB#dispatchTouchEvent(), ev: ${getAction(ev)}")
return super.dispatchTouchEvent(ev)
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
Log.d("hehe", "MyViewGroupB#onInterceptTouchEvent(), ev: ${getAction(ev)}")
if (ev.action == MotionEvent.ACTION_MOVE) {
return true
}
return super.onInterceptTouchEvent(ev)
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
Log.d("hehe", "MyViewGroupB#onTouchEvent(), ev: ${getAction(ev)}")
return super.onTouchEvent(ev)
}
}
class MyView(context: Context) : View(context) {
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
Log.d("hehe", "MyView#dispatchTouchEvent(), ev: ${getAction(ev)}")
return super.dispatchTouchEvent(ev)
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
Log.d("hehe", "MyView#onTouchEvent(), ev: ${getAction(ev)}")
if (ev.action == MotionEvent.ACTION_DOWN) {
return true
}
return super.onTouchEvent(ev)
}
}
日志:
# 首次 DOWN 事件到了 View
MyViewGroupA#dispatchTouchEvent(), ev: down, 756200913
MyViewGroupA#onInterceptTouchEvent(), ev: down, 756200913
MyViewGroupB#dispatchTouchEvent(), ev: down, 756200913
MyViewGroupB#onInterceptTouchEvent(), ev: down, 756200913
MyView#dispatchTouchEvent(), ev: down, 756200913
MyView#onTouchEvent(), ev: down, 756200913
# 接下来的第一次 MOVE 事件被 MyViewGroupB 拦截
MyViewGroupA#dispatchTouchEvent(), ev: move, 756200930
MyViewGroupA#onInterceptTouchEvent(), ev: move, 756200930
MyViewGroupB#dispatchTouchEvent(), ev: move, 756200930
MyViewGroupB#onInterceptTouchEvent(), ev: move, 756200930
MyView#dispatchTouchEvent(), ev: cancel, 756200930
MyView#onTouchEvent(), ev: cancel, 756200930
# 紧接着的第二次 MOVE 事件正常到 MyViewGroupB
MyViewGroupA#dispatchTouchEvent(), ev: move, 756200940
MyViewGroupA#onInterceptTouchEvent(), ev: move, 756200940
MyViewGroupB#dispatchTouchEvent(), ev: move, 756200940
MyViewGroupB#onTouchEvent(), ev: move, 756200940
# 再下来的第三次 MOVE 事件被 MyViewGroupA 拦截
MyViewGroupA#dispatchTouchEvent(), ev: move, 756200950
MyViewGroupA#onInterceptTouchEvent(), ev: move, 756200950
MyViewGroupB#dispatchTouchEvent(), ev: cancel, 756200950
MyViewGroupB#onTouchEvent(), ev: cancel, 756200950
# 以后的事件都直接到 MyViewGroupA 了
MyViewGroupA#dispatchTouchEvent(), ev: move, 756200959
MyViewGroupA#onTouchEvent(), ev: move, 756200959
MyViewGroupA#dispatchTouchEvent(), ev: move, 756200970
MyViewGroupA#onTouchEvent(), ev: move, 756200970
MyViewGroupA#dispatchTouchEvent(), ev: move, 756201105
MyViewGroupA#onTouchEvent(), ev: move, 756201105
MyViewGroupA#dispatchTouchEvent(), ev: up, 756201113
MyViewGroupA#onTouchEvent(), ev: up, 756201113