应用在响应键盘事件的时候有焦点漂移,产生半透明蒙层
-
蒙层的本质 :那个"半透明蒙层"通常是 Android 系统的
Default Focus Highlight(默认焦点高亮)。这意味着某个 View 依然获取到了焦点。 -
Root View 的自动找焦 :当方向键按下时,如果当前 View 拦截了事件但没有彻底阻止系统的焦点搜索算法,或者焦点跳到了
DecorView或rootLayout上,就会出现这种情况。
半透明蒙层效果

正常效果

🛠️ 方案一:禁用全局焦点高亮 (针对蒙层)
如果你的目标是让用户看不见那个"半透明蒙层",可以直接在主题或根布局中关闭它。
在 themes.xml 中设置:
XML
ini
<item name="android:defaultFocusHighlightEnabled">false</item>
或者在你的根布局 (Root Layout) XML 中设置:
XML
ini
<LinearLayout
...
android:focusable="true"
android:defaultFocusHighlightEnabled="false">
这会消除那个半透明的视觉反馈,但并不能阻止焦点在后台逻辑上的移动。
🛠️ 方案二:强制拦截 ViewTree 的焦点搜索
有时 dispatchKeyEvent 拦截太晚了,焦点搜索是在更底层处理的。你可以重写根布局的 focusSearch 方法,直接阻断寻找过程。
自定义你的根容器类(例如 ConstraintLayout):
Kotlin
kotlin
class ForbiddenFocusLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
override fun focusSearch(focused: View?, direction: Int): View? {
// 当按下方向键触发焦点搜索时,直接返回 null,告诉系统"找不到下一个焦点"
// 这样焦点就会强制留在当前 View 上,不会漂移
return null
}
}
然后在 XML 中使用这个自定义 Layout 作为页面的最外层容器。
🛠️ 方案三:禁用布局管理器的焦点 (针对列表)
如果漂移发生在 RecyclerView 或 ListView 中,上述方案可能失效,因为它们有自己的焦点管理。
Kotlin
ini
// 彻底禁用整个列表的焦点获取能力
recyclerView.isFocusable = false
recyclerView.isFocusableInTouchMode = false
recyclerView.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS
🛠️ 方案四:拦截 Window 层的事件 (物理隔离)
如果 dispatchKeyEvent 在 Activity 中没拦住,可能是因为某些子 View(如 WebView 或某些第三方 SDK 的 SurfaceView)抢占了消费权。你可以尝试在 onKeyDown 中再次拦截。
Kotlin
kotlin
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
return when (keyCode) {
KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_DOWN,
KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_RIGHT -> true // 拦截所有
else -> super.onKeyDown(keyCode, event)
}
}
💡 为什么会出现"蒙层"?
Android 系统的焦点移动逻辑如下:
- 方向键按下 :系统调用
focusSearch()。 - 查找算法:系统在整个 View 树中寻找距离最近的、可聚焦的 View。
- 视觉反馈:一旦找到新 View,系统会绘制一个默认的高亮层(即你看到的蒙层)。
最终建议: 如果你是在做 AAOS 车载启动器 或 全屏应用:
- 第一步 :使用 进阶方案二 ,重构根布局并返回
null。 - 第二步 :设置
android:defaultFocusHighlightEnabled="false"。
这两步配合,可以确保即便系统在底层处理了事件,也不会有视觉上的蒙层出现。