Android中Window.Callback无法接收Motion事件及Toolbar回调检测方案
解决Toolbar替换Window.Callback导致拦截失效的问题
首先咱们得把问题根源理清楚:当你调用setSupportActionBar(Toolbar)或setActionBar(Toolbar)时,ToolbarActionBar会创建自己的ToolbarCallbackWrapper,直接替换掉Window的Callback——这就把你之前设置的包装类给覆盖了,导致你的Motion事件拦截逻辑完全失效。
要在不继承基类Activity的前提下解决这个问题,我推荐一个轻量且非频繁检测的方案:让你的Window.Callback包装类具备「自我恢复」的能力,每次事件触发时自动检查并夺回Callback的控制权。
实现步骤
1. 改造你的Window.Callback包装类
创建一个带自我恢复逻辑的PersistentWindowCallback,核心是在每次处理事件前检查当前Window的Callback是否还是自己,如果被替换了就立刻重新设置回去:
public class PersistentWindowCallback implements Window.Callback { private final WeakReference<Window> mWindowRef; private Window.Callback mOriginalCallback; public PersistentWindowCallback(Window window, Window.Callback originalCallback) { mWindowRef = new WeakReference<>(window); mOriginalCallback = originalCallback; } // 核心逻辑:确保当前Window的Callback是我们的实例 private void ensureCallbackIsActive() { Window window = mWindowRef.get(); if (window != null && window.getCallback() != this) { // 保存新的原始Callback(也就是Toolbar设置的那个) mOriginalCallback = window.getCallback(); // 重新设置我们的Callback,夺回事件拦截权 window.setCallback(this); } } // 在这里添加你的Motion事件拦截逻辑 @Override public boolean dispatchTouchEvent(MotionEvent event) { ensureCallbackIsActive(); // 示例:拦截并处理触摸事件 // Log.d("MotionInterceptor", "捕获到触摸事件:" + event.getAction()); return mOriginalCallback.dispatchTouchEvent(event); } // 所有Window.Callback方法都需要调用ensureCallbackIsActive,再转发给原始Callback @Override public boolean dispatchKeyEvent(KeyEvent event) { ensureCallbackIsActive(); return mOriginalCallback.dispatchKeyEvent(event); } @Override public boolean dispatchKeyShortcutEvent(KeyEvent event) { ensureCallbackIsActive(); return mOriginalCallback.dispatchKeyShortcutEvent(event); } // 请实现Window.Callback的所有其他方法,逻辑和上面一致 // ... }
2. 在Activity中初始化
和你之前的初始化逻辑几乎一致,只是换成我们的持久化Callback:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 正常初始化Toolbar Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); // 设置我们的持久化Callback final Window window = getWindow(); final Window.Callback originalCallback = window.getCallback(); final PersistentWindowCallback callbackWrapper = new PersistentWindowCallback(window, originalCallback); window.setCallback(callbackWrapper); }
为什么这个方案有效?
- 非频繁检测:只有当有事件触发(触摸、按键等)时才会检查Callback状态,不会做无意义的轮询或全局布局监听,完全符合你的需求。
- 自动恢复:一旦Toolbar替换了Callback,第一次事件触发时就会立刻把我们的Callback重新设置回去,之后所有事件都会先经过我们的拦截逻辑,再转发给Toolbar的Callback。
- 无基类依赖:不需要继承自定义Activity,直接在任意Activity中初始化即可,灵活性拉满。
- 避免内存泄漏:用
WeakReference持有Window,不会因为Window的生命周期导致内存泄漏问题。
备选方案(不推荐,仅作参考)
如果你能接受反射,可以通过监听Window的mCallback字段变化来触发恢复逻辑,但反射存在兼容性风险(不同Android版本可能字段名或访问权限变化),所以上面的方案是更稳妥的选择。
内容的提问来源于stack exchange,提问作者ZolkiBy




