前言
最近逛wanAndroid
论坛,发现一个有趣的问题:如何捕获一个Activity页面上所有的点击行为。
一起研究下吧,不想看源码的小伙伴可以直接看文末总结~
准备工作
先得罗列出页面上的一些点击行为,常用的有:
- 普通View的点击
- 动态add的View的点击
- Dialog上的按钮点击
于是就有了如下代码:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn1.setOnClickListener {
showToast("点了按钮1")
}
btn2.setOnClickListener {
val builder =
AlertDialog.Builder(this)
.setTitle("我是一个dialog")
val view: View = layoutInflater.inflate(R.layout.dialog_btn, null)
val btn4 =
view.findViewById<View>(R.id.btn4)
btn4.setOnClickListener {
showToast("点击了Dialog按钮")
}
builder.setView(view)
builder.create().show()
}
btn3.setOnClickListener {
var button = Button(this)
button.text = "我是新加的按钮"
var param = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
mainlayout.addView(button, param)
button.setOnClickListener {
showToast("点击了新加的按钮")
}
}
}
}
既然我要捕获点击事件,首先就想到的是通过事件分发机制,也就是在源头就去获取所有的触摸事件,然后对点击事件进行统计,干吧~
事件分发
重写Activity的dispatchTouchEvent
方法,由于只有点击事件,所以只需要统计ACTION_UP
事件即可,如果有长按事件就在需要判断下按下的时间。
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
ev?.let {
when (ev.action) {
MotionEvent.ACTION_UP -> {
Log.e(Companion.TAG,"ACTION_UP——CLICK")
}
else -> {
}
}
}
return super.dispatchTouchEvent(ev)
}
ok,运行下。
- 点击按钮1,日志打印正常
- 点击按钮2中的dialog按钮,日志。。。没有
- 点击按钮3中的button,日志打印正常
结果大家也看到了,Dialog
中的点击事件无法被响应,这是为啥呢?
这就要从事件分发机制说起了,点击屏幕首先响应的是当前屏幕的顶层View,也就是DecorView
,在Activity中也就是Window的根布局。然后DecorView
会调用Activity的dispatchTouchEvent
方法,作为开发者事件分发的一个控制拦截,最后重新返回到DecorView
的super.dispatchTouchEvent(event)
方法开始ViewGroup的事件传递。看看相关源码:
//DecorView.java
@Override
public boolean dispatchTouchEvent