android开发之代理Window.Callback

Window.Callback是window类的一个内部接口。该接口包含了一系列类似于dispatchXXX和onXXX的接口。当window接收到外界状态改变的通知时,就会回调其中的相应方法。比如,当用户点击某个控件时,就会回调Window.Callback中的dispatchTouchEvent方法。

  • window.Callback的定义如下:
 public interface Callback {
        boolean dispatchKeyEvent(KeyEvent var1);

        boolean dispatchKeyShortcutEvent(KeyEvent var1);

        boolean dispatchTouchEvent(MotionEvent var1);

        boolean dispatchTrackballEvent(MotionEvent var1);

        boolean dispatchGenericMotionEvent(MotionEvent var1);

        boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent var1);

        @Nullable
        View onCreatePanelView(int var1);

        boolean onCreatePanelMenu(int var1, @NonNull Menu var2);

        boolean onPreparePanel(int var1, @Nullable View var2, @NonNull Menu var3);

        boolean onMenuOpened(int var1, @NonNull Menu var2);

        boolean onMenuItemSelected(int var1, @NonNull MenuItem var2);

        void onWindowAttributesChanged(LayoutParams var1);

        void onContentChanged();

        void onWindowFocusChanged(boolean var1);

        void onAttachedToWindow();

        void onDetachedFromWindow();

        void onPanelClosed(int var1, @NonNull Menu var2);

        boolean onSearchRequested();

        boolean onSearchRequested(SearchEvent var1);

        @Nullable
        ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback var1);

        @Nullable
        ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback var1, int var2);

        void onActionModeStarted(ActionMode var1);

        void onActionModeFinished(ActionMode var1);

        default void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, @Nullable Menu menu, int deviceId) {
            throw new RuntimeException("Stub!");
        }

        default void onPointerCaptureChanged(boolean hasCapture) {
            throw new RuntimeException("Stub!");
        }
    }
  • 这个方法可以用来实现全埋点,通过activity.getWindow方法拿到这个activity对应的window对象,再通过window.getCallback方法就可以拿到当前对应的Callback对象,然后我们在代理这个callback对象就可以找到被点击的view对象,并插入埋点代码。

  • 案例如下:

package com.mvp.myapplication

import android.annotation.TargetApi
import android.app.Activity
import android.graphics.Rect
import android.os.Bundle
import android.util.Log
import android.view.*
import android.view.Window.Callback
import android.view.accessibility.AccessibilityEvent
import android.widget.AdapterView
import android.widget.TextView
import androidx.annotation.Nullable
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {
    private lateinit var tv:TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        this.window.callback = WrapperWindowCallback(this, window.callback)

        tv = findViewById(R.id.tv)

        tv.setOnClickListener {
            Log.e("MainActivity","原始数据")
        }

    }
}

class WrapperWindowCallback : Window.Callback {
    private var callback: Window.Callback? = null
    private var activity: Activity? = null

    constructor() {}

    public constructor(activity: Activity, callback: Callback) : this() {
        this.callback = callback
        this.activity = activity
    }

    override fun dispatchKeyEvent(p0: KeyEvent?): Boolean {
        return this.callback!!.dispatchKeyEvent(p0)
    }

    override fun dispatchKeyShortcutEvent(p0: KeyEvent?): Boolean {
        return this.callback!!.dispatchKeyShortcutEvent(p0)
    }

    override fun dispatchTouchEvent(p0: MotionEvent?): Boolean {
        dispatchTouchEvent(activity!!, p0!!)
        return this.callback!!.dispatchTouchEvent(p0)
    }

    override fun dispatchTrackballEvent(p0: MotionEvent?): Boolean {
        return this.callback!!.dispatchTrackballEvent(p0)
    }

    override fun dispatchGenericMotionEvent(p0: MotionEvent?): Boolean {
        return this.callback!!.dispatchGenericMotionEvent(p0)
    }

    override fun dispatchPopulateAccessibilityEvent(p0: AccessibilityEvent?): Boolean {
        return this.callback!!.dispatchPopulateAccessibilityEvent(p0)
    }

    override fun onCreatePanelView(p0: Int): View? {
        return this.callback!!.onCreatePanelView(p0)
    }

    override fun onCreatePanelMenu(p0: Int, p1: Menu): Boolean {
        return this.callback!!.onCreatePanelMenu(p0, p1)
    }

    override fun onPreparePanel(p0: Int, p1: View?, p2: Menu): Boolean {
        return this.callback!!.onPreparePanel(p0, p1, p2)
    }

    override fun onMenuOpened(p0: Int, p1: Menu): Boolean {
        return this.callback!!.onMenuOpened(p0, p1)
    }

    override fun onMenuItemSelected(p0: Int, p1: MenuItem): Boolean {
        return this.callback!!.onMenuItemSelected(p0, p1)
    }

    override fun onWindowAttributesChanged(p0: WindowManager.LayoutParams?) {
        this.callback!!.onWindowAttributesChanged(p0)
    }

    override fun onContentChanged() {
        this.callback!!.onContentChanged()
    }

    override fun onWindowFocusChanged(p0: Boolean) {
        this.callback!!.onWindowFocusChanged(p0)
    }

    override fun onAttachedToWindow() {
        this.callback!!.onAttachedToWindow()
    }

    override fun onDetachedFromWindow() {
        this.callback!!.onDetachedFromWindow()
    }

    override fun onPanelClosed(p0: Int, p1: Menu) {
        this.callback!!.onPanelClosed(p0, p1)
    }

    override fun onSearchRequested(): Boolean {
        return this.callback!!.onSearchRequested()
    }

    @Nullable
    @TargetApi(23)
    override fun onSearchRequested(p0: SearchEvent?): Boolean {
        return this.callback!!.onSearchRequested(p0)
    }

    override fun onWindowStartingActionMode(p0: ActionMode.Callback?): ActionMode? {
        return this.callback!!.onWindowStartingActionMode(p0)
    }

    @Nullable
    @TargetApi(23)
    override fun onWindowStartingActionMode(p0: ActionMode.Callback?, p1: Int): ActionMode? {
        return this.callback!!.onWindowStartingActionMode(p0, p1)
    }

    override fun onActionModeStarted(p0: ActionMode?) {
        this.callback!!.onActionModeStarted(p0)
    }

    override fun onActionModeFinished(p0: ActionMode?) {
        this.callback!!.onActionModeFinished(p0)
    }

    private fun dispatchTouchEvent(activity: Activity, event: MotionEvent) {
        if (event.action == MotionEvent.ACTION_UP) {
            val rootView = activity.window.decorView as ViewGroup
            val targetVies = getTargetViews(rootView, event) ?: return
            for (view in targetVies) {
                if (view == null) {
                    continue
                }
                Log.e("MainActivity", "埋点数据")
            }
        }
    }

    private fun getTargetViewsInGroup(
        parent: ViewGroup,
        event: MotionEvent,
        hitViews: ArrayList<View>
    ) {
        try {
            val childCount = parent.childCount
            for (i in 0 until childCount) {
                val child = parent.getChildAt(i)
                val hitChildren: ArrayList<View> = getTargetViews(child, event)
                if (hitChildren.isNotEmpty()) {
                    hitViews.addAll(hitChildren)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun getTargetViews(parent: View, event: MotionEvent): ArrayList<View> {
        val targetViews: ArrayList<View> = ArrayList()
        try {
            if (isVisible(parent) && isContainView(parent, event)) {
                if (parent is AdapterView<*>) {
                    targetViews.add(parent)
                    getTargetViewsInGroup((parent as ViewGroup), event, targetViews)
                } else if (parent.isClickable) {
                    targetViews.add(parent)
                } else if (parent is ViewGroup) {
                    getTargetViewsInGroup(parent, event, targetViews)
                }
            }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
        return targetViews
    }

    private fun isVisible(view: View): Boolean {
        return view.visibility == View.VISIBLE
    }

    private fun isContainView(view: View, event: MotionEvent): Boolean {
        val x = event.rawX.toDouble()
        val y = event.rawY.toDouble()
        val outRect = Rect()
        view.getGlobalVisibleRect(outRect)
        return outRect.contains(x.toInt(), y.toInt())
    }

}
  • 缺点
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值