理解Android View的事件传递机制
在android事件传递一般包括三个对象: Activity,ViewGroup,View,事件分发顺序为:Activity->ViewGroup->View,事件分发过程由
onTouchEvent()
onInterceptTouchEvent()
dispatchTouchEvent()
这三个方法协助完成, 其中View没有onInterceptTouchEvent()方法
下面用一个简单的例子进行说明:
testButton, testLayout为自定义控件
class MainActivity : AppCompatActivity(), View.OnTouchListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//view没有onInterceptTouchEvent
//onTouch事件优于onClick
btnTest.setOnTouchListener(this)
btnTest.setOnClickListener {
Toast.makeText(this@MainActivity,"te", Toast.LENGTH_LONG).show()
}
}
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (v){
mainLayout->{
when (event?.action){
MotionEvent.ACTION_DOWN->{
Log.d("touch event","mainLayout ACTION_DOWN")
}
MotionEvent.ACTION_UP->{
Log.d("touch event","mainLayout ACTION_UP")
}
}
}
btnTest->{
when (event?.action){
MotionEvent.ACTION_DOWN->{
Log.d("touch event","btnTest ACTION_DOWN")
}
MotionEvent.ACTION_UP->{
Log.d("touch event","btnTest ACTION_UP")
}
}
}
}
return true
}
//activity的dispatchTouchEvent
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
//事件分派
when (ev?.action){
MotionEvent.ACTION_DOWN->{
Log.d("touch event","activity dispatchTouchEvent ACTION_DOWN")
//在ACTION_DOWN return true,子view收不到后面的事件
//return true
}
MotionEvent.ACTION_UP->{
Log.d("touch event","activity dispatchTouchEvent ACTION_UP")
//ACTION_UP return true,子view收不到ACTION_UP
//return true
}
}
return super.dispatchTouchEvent(ev)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
return super.onTouchEvent(event)
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class TestButton:Button { constructor(mContext: Context) : super (mContext) { val context = mContext } constructor(mContext: Context, mAttributeSet: AttributeSet) : super (mContext, mAttributeSet) { val context = mContext } override fun onTouchEvent(event: MotionEvent?): Boolean { //return super.onTouchEvent(event) when (event?.action){ MotionEvent.ACTION_DOWN->{ Log.d( "touch event" , "btnTest ACTION_DOWN" ) return true } MotionEvent.ACTION_UP->{ Log.d( "touch event" , "btnTest ACTION_UP" ) } } //如果返回true,不会把onTouch事件传递回activity,就是说activity的onTouchEvent不会被调用 return true } override fun dispatchTouchEvent(event: MotionEvent?): Boolean { return super .dispatchTouchEvent(event) } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | class TestLayout : ConstraintLayout{ constructor(mContext: Context) : super (mContext) { val context = mContext } constructor(mContext: Context, mAttributeSet: AttributeSet) : super (mContext, mAttributeSet) { val context = mContext } override fun onTouchEvent(event: MotionEvent?): Boolean { //return super.onTouchEvent(event) Log.d( "touch event" , "view group" ) return true } override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { //return super.onInterceptTouchEvent(ev) //true:中断事件 //中断后, TestLayout的子view btnTest的onTouch,onClick都不会执行 return true } override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { //事件分派 when (ev?.action){ MotionEvent.ACTION_DOWN->{ Log.d( "touch event" , "view group dispatchTouchEvent ACTION_DOWN" ) //在ACTION_DOWN return true,子view收不到后面的事件 //return true } MotionEvent.ACTION_UP->{ Log.d( "touch event" , "view group dispatchTouchEvent ACTION_UP" ) //ACTION_UP return true,子view收不到ACTION_UP //return true } } return super .dispatchTouchEvent(ev) } } |
我们通过log查看到,每次touch btnTest或mainLayout,都会先打印Activity里的dispatchTouchEvent里的ACTION_DOWN,是传递的起点
a. 如果Activity的dispatchTouchEvent里的ACTION_DOWN return true,子view就收不到事件传递了;
b. TestLayout的onInterceptTouchEvent,如果return true,表示中断传递给子view的事件,TestButton的onTouch,onClick都不会执行,会走TestLayout的onTouchEvent;就是说如果ViewGroup的onInterceptTouchEvent return true,就会走自已的onTouchEvent();
c. 如果TestLayout的onTouchEvent return true,就是消费掉事件,不会把onTouch事件传递回activity,就是说activity的onTouchEvent不会被调用
d. 同理TestButton的onTouchEvent return true,也不会把onTouch事件传递回activity,就是说activity的onTouchEvent不会被调用
如果view,viewgroup都没有对事件进行消费,会以activity的onTouchEvent作为终点;
如果viewgroup中断事件并对事件进行消费,会以viewgroup onTouchEvent作为终点;
如果viewgroup没有中断事件,但view(如button)对事件进行消费,会以button ACTION_UP作为终点;
如果viewgroup没有中断事件,view也没有对事件进行消费,会以activity的onTouchEvent作为终点;
另一理解点
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)