Android 事件传递机制
本文从实践中观察事件是如何传递的。直接上代码观察。
1. 自定义View
MotionEventActivity
package edu.tyut.helloworld
import android.os.Bundle
import android.util.Log
import android.view.MotionEvent
import androidx.appcompat.app.AppCompatActivity
import edu.tyut.helloworld.databinding.ActivityMotionEventBinding
private const val TAG: String = "MotionEventActivity"
class MotionEventActivity : AppCompatActivity() {
private val binding: ActivityMotionEventBinding by lazy {
ActivityMotionEventBinding.inflate(layoutInflater)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
initView()
}
private fun initView(){
Log.i(TAG, "initView...")
}
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
when(event?.action){
MotionEvent.ACTION_DOWN -> {
Log.i(TAG, "dispatchTouchEvent -> action down...")
}
MotionEvent.ACTION_MOVE -> {
Log.i(TAG, "dispatchTouchEvent -> action move...")
}
MotionEvent.ACTION_UP -> {
Log.i(TAG, "dispatchTouchEvent -> action up...")
}
MotionEvent.ACTION_CANCEL -> {
Log.i(TAG, "dispatchTouchEvent -> action cancel...")
}
else -> {
Log.i(TAG, "dispatchTouchEvent -> else...")
}
}
return super.dispatchTouchEvent(event).apply {
Log.i(TAG, "dispatchTouchEvent -> isConsume: $this")
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
when(event?.action){
MotionEvent.ACTION_DOWN -> {
Log.i(TAG, "onTouchEvent -> action down...")
}
MotionEvent.ACTION_MOVE -> {
Log.i(TAG, "onTouchEvent -> action move...")
}
MotionEvent.ACTION_UP -> {
Log.i(TAG, "onTouchEvent -> action up...")
}
MotionEvent.ACTION_CANCEL -> {
Log.i(TAG, "onTouchEvent -> action cancel...")
}
else -> {
Log.i(TAG, "onTouchEvent -> else...")
}
}
return super.onTouchEvent(event).apply {
Log.i(TAG, "onTouchEvent -> isConsume: $this")
}
}
}
layout/activity_motion_event.xml
<?xml version="1.0" encoding="utf-8"?>
<edu.tyut.helloworld.view.CustomLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<edu.tyut.helloworld.view.CustomView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</edu.tyut.helloworld.view.CustomLinearLayout>
CustomLinearLayout
package edu.tyut.helloworld.view
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import androidx.appcompat.widget.LinearLayoutCompat
private const val TAG: String = "CustomLinearLayout"
class CustomLinearLayout : LinearLayoutCompat {
constructor(context: Context) : this(context = context, attributeSet = null)
constructor(context: Context, attributeSet: AttributeSet?) : this(context = context, attributeSet = attributeSet, defStyleAttr = 0)
constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr){
initView()
}
private fun initView(){
Log.i(TAG, "initView...")
}
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
when(event?.action){
MotionEvent.ACTION_DOWN -> {
Log.i(TAG, "dispatchTouchEvent -> action down...")
}
MotionEvent.ACTION_MOVE -> {
Log.i(TAG, "dispatchTouchEvent -> action move...")
}
MotionEvent.ACTION_UP -> {
Log.i(TAG, "dispatchTouchEvent -> action up...")
}
MotionEvent.ACTION_CANCEL -> {
Log.i(TAG, "dispatchTouchEvent -> action cancel...")
}
else -> {
Log.i(TAG, "dispatchTouchEvent -> else...")
}
}
return super.dispatchTouchEvent(event).apply {
Log.i(TAG, "dispatchTouchEvent -> isConsume: $this")
}
}
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
when(event?.action){
MotionEvent.ACTION_DOWN -> {
performClick()
Log.i(TAG, "onInterceptTouchEvent -> action down...")
}
MotionEvent.ACTION_MOVE -> {
Log.i(TAG, "onInterceptTouchEvent -> action move...")
}
MotionEvent.ACTION_UP -> {
Log.i(TAG, "onInterceptTouchEvent -> action up...")
}
MotionEvent.ACTION_CANCEL -> {
Log.i(TAG, "onInterceptTouchEvent -> action cancel...")
}
else -> {
Log.i(TAG, "onInterceptTouchEvent -> else...")
}
}
return super.onInterceptTouchEvent(event).apply {
Log.i(TAG, "onInterceptTouchEvent -> isConsume: $this")
}
}
override fun performClick(): Boolean {
return super.performClick().apply {
Log.i(TAG, "performClick -> isConsume: $this")
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
when(event?.action){
MotionEvent.ACTION_DOWN -> {
performClick()
Log.i(TAG, "onTouchEvent -> action down...")
}
MotionEvent.ACTION_MOVE -> {
Log.i(TAG, "onTouchEvent -> action move...")
}
MotionEvent.ACTION_UP -> {
Log.i(TAG, "onTouchEvent -> action up...")
}
MotionEvent.ACTION_CANCEL -> {
Log.i(TAG, "onTouchEvent -> action cancel...")
}
else -> {
Log.i(TAG, "onTouchEvent -> else...")
}
}
return super.onTouchEvent(event).apply {
Log.i(TAG, "onTouchEvent -> isConsume: $this")
}
}
}
CustomView
package edu.tyut.helloworld.view
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
private const val TAG: String = "CustomView"
class CustomView : View {
constructor(context: Context) : this(context = context, attributeSet = null)
constructor(context: Context, attributeSet: AttributeSet?) : this(context = context, attributeSet = attributeSet, defStyleAttr = 0)
constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr){
initView()
}
private fun initView(){
Log.i(TAG, "initView...")
}
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
when(event?.action){
MotionEvent.ACTION_DOWN -> {
Log.i(TAG, "dispatchTouchEvent -> action down...")
}
MotionEvent.ACTION_MOVE -> {
Log.i(TAG, "dispatchTouchEvent -> action move...")
}
MotionEvent.ACTION_UP -> {
Log.i(TAG, "dispatchTouchEvent -> action up...")
}
MotionEvent.ACTION_CANCEL -> {
Log.i(TAG, "dispatchTouchEvent -> action cancel...")
}
else -> {
Log.i(TAG, "dispatchTouchEvent -> else...")
}
}
return super.dispatchTouchEvent(event).apply {
Log.i(TAG, "dispatchTouchEvent -> isConsume: $this")
}
}
override fun performClick(): Boolean {
return super.performClick().apply {
Log.i(TAG, "performClick -> isConsume: $this")
}
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
when(event?.action){
MotionEvent.ACTION_DOWN -> {
performClick()
Log.i(TAG, "onTouchEvent -> action down...")
}
MotionEvent.ACTION_MOVE -> {
Log.i(TAG, "onTouchEvent -> action move...")
}
MotionEvent.ACTION_UP -> {
Log.i(TAG, "onTouchEvent -> action up...")
}
MotionEvent.ACTION_CANCEL -> {
Log.i(TAG, "onTouchEvent -> action cancel...")
}
else -> {
Log.i(TAG, "onTouchEvent -> else...")
}
}
return super.onTouchEvent(event).apply {
Log.i(TAG, "onTouchEvent -> isConsume: $this")
}
}
}
分析日志
点击手机屏幕,日志如下:
2025-03-03 14:15:09.808 23938-23938 MotionEventActivity edu.tyut.helloworld I dispatchTouchEvent -> action down...
2025-03-03 14:15:09.809 23938-23938 CustomLinearLayout edu.tyut.helloworld I dispatchTouchEvent -> action down...
2025-03-03 14:15:09.810 23938-23938 CustomLinearLayout edu.tyut.helloworld I performClick -> isConsume: false
2025-03-03 14:15:09.810 23938-23938 CustomLinearLayout edu.tyut.helloworld I onInterceptTouchEvent -> action down...
2025-03-03 14:15:09.810 23938-23938 CustomLinearLayout edu.tyut.helloworld I onInterceptTouchEvent -> isConsume: false
2025-03-03 14:15:09.810 23938-23938 CustomView edu.tyut.helloworld I dispatchTouchEvent -> action down...
2025-03-03 14:15:09.810 23938-23938 CustomView edu.tyut.helloworld I performClick -> isConsume: false
2025-03-03 14:15:09.810 23938-23938 CustomView edu.tyut.helloworld I onTouchEvent -> action down...
2025-03-03 14:15:09.810 23938-23938 CustomView edu.tyut.helloworld I onTouchEvent -> isConsume: false
2025-03-03 14:15:09.810 23938-23938 CustomView edu.tyut.helloworld I dispatchTouchEvent -> isConsume: false
2025-03-03 14:15:09.810 23938-23938 CustomLinearLayout edu.tyut.helloworld I performClick -> isConsume: false
2025-03-03 14:15:09.810 23938-23938 CustomLinearLayout edu.tyut.helloworld I onTouchEvent -> action down...
2025-03-03 14:15:09.810 23938-23938 CustomLinearLayout edu.tyut.helloworld I onTouchEvent -> isConsume: false
2025-03-03 14:15:09.810 23938-23938 CustomLinearLayout edu.tyut.helloworld I dispatchTouchEvent -> isConsume: false
2025-03-03 14:15:09.811 23938-23938 MotionEventActivity edu.tyut.helloworld I onTouchEvent -> action down...
2025-03-03 14:15:09.811 23938-23938 MotionEventActivity edu.tyut.helloworld I onTouchEvent -> isConsume: false
2025-03-03 14:15:09.811 23938-23938 MotionEventActivity edu.tyut.helloworld I dispatchTouchEvent -> isConsume: false
2025-03-03 14:15:09.812 23938-23938 MotionEventActivity edu.tyut.helloworld I dispatchTouchEvent -> action up...
2025-03-03 14:15:09.813 23938-23938 MotionEventActivity edu.tyut.helloworld I onTouchEvent -> action up...
2025-03-03 14:15:09.813 23938-23938 MotionEventActivity edu.tyut.helloworld I onTouchEvent -> isConsume: false
2025-03-03 14:15:09.813 23938-23938 MotionEventActivity edu.tyut.helloworld I dispatchTouchEvent -> isConsume: false
其事件传递图为: