class ProxyClickListener(
private val className: String?,
private val originalListener: View.OnClickListener
) : View.OnClickListener {
override fun onClick(p0: View?) {
p0?.apply {
Log.i(
className + " " + this.javaClass.simpleName + " id:" + this.context.resources.getResourceEntryName(
this.id
) + " OnClick()"
)
originalListener.onClick(this)
}
}
}
object ProxyManager {
@SuppressLint("DiscouragedPrivateApi", "PrivateApi")
fun hookOnClickListener(className: String?, view: View) {
try {
val viewClass = Class.forName("android.view.View")
// 得到 View 的 ListenerInfo 对象
val getListenerInfo = viewClass.getDeclaredMethod("getListenerInfo")
//修改getListenerInfo为可访问(View中的getListenerInfo不是public)
getListenerInfo.isAccessible = true
val listenerInfo = getListenerInfo.invoke(view)
// 得到 原始的 OnClickListener 对象
val listenerInfoClz = Class.forName("android.view.View\$ListenerInfo")
val mOnClickListener: Field? = listenerInfoClz.getDeclaredField("mOnClickListener")
mOnClickListener?.let {
it.isAccessible = true
val originOnClickListener = it.get(listenerInfo) as View.OnClickListener?
// 用自定义的 OnClickListener 替换原始的 OnClickListener
originOnClickListener?.apply {
val hookedOnClickListener = ProxyClickListener(className, this)
it.set(listenerInfo, hookedOnClickListener)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
fun hookView(className: String?, rootView: View) {
if (rootView.visibility == View.VISIBLE) {
if (rootView.isClickable) {
hookOnClickListener(className, rootView)
}
if (rootView is ViewGroup) {
val childCount = rootView.childCount
repeat(childCount) {
val childView = rootView.getChildAt(it)
hookView(className,childView)
}
}
}
}
}
abstract class BaseActivity : AppCompatActivity() {
lateinit var mView: View
protected abstract val layoutId: Int
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(getLayout())
initListener()
//在view setOnClickListener后hook布局
ProxyManager.hookView(this.javaClass.canonicalName,mView)
}
private fun getLayout(): View {
mView = View.inflate(this, layoutId, null)
return mView
}
open fun initListener() {
}
}