Android TV WebView 遥控器按键处理:从全透传到白名单
问题
做 TV 看板 App 时,WebView 默认会把所有遥控器按键事件透传给网页。方向键、确定键给网页没问题,但音量键、电源键也进了网页 --- 这就很奇怪,网页要音量键干嘛?
常见的两种思路
黑名单
在 WebView 层拦截特定按键,不传进去:
kotlin
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
return when (event.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP,
KeyEvent.KEYCODE_VOLUME_DOWN,
KeyEvent.KEYCODE_POWER,
... // 来一个禁一个
-> super.dispatchKeyEvent(event)
else -> awContents.dispatchKeyEvent(event)
}
}
问题很明显:遥控器按键几十个,漏一个就是一个坑。
白名单(推荐)
反过来,只放行网页真正需要的按键,其余全部走系统默认:
kotlin
// 白名单:只把这些导航键传给 WebView
private val webViewKeys = setOf(
KeyEvent.KEYCODE_DPAD_UP, // 方向键上
KeyEvent.KEYCODE_DPAD_DOWN, // 方向键下
KeyEvent.KEYCODE_DPAD_LEFT, // 方向键左
KeyEvent.KEYCODE_DPAD_RIGHT, // 方向键右
KeyEvent.KEYCODE_DPAD_CENTER, // 确定键
KeyEvent.KEYCODE_ENTER, // 回车(外接键盘)
KeyEvent.KEYCODE_BACK // 返回
)
关键是在 Activity 层拦截,而不是 WebView 层:
kotlin
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
// 白名单内的键正常分发(进入 WebView)
if (event.keyCode in webViewKeys) {
return super.dispatchKeyEvent(event)
}
// 菜单键:弹出 URL 选择列表
if (event.keyCode == KeyEvent.KEYCODE_MENU && event.action == KeyEvent.ACTION_DOWN) {
showUrlSelectionDialog()
return true
}
// 其余按键(音量、电源、主页、数字键...)交给系统
return super.dispatchKeyEvent(event)
}
为什么是这几个键
| 按键 | 原因 |
|---|---|
| DPAD_UP/DOWN/LEFT/RIGHT | 看板页面有可滚动内容、可聚焦元素,依赖遥控器方向键导航 |
| DPAD_CENTER | 遥控器中间的确定键,是点击网页按钮/链接的唯一方式 |
| ENTER | 外接键盘发送的确认键,行为和 DPAD_CENTER 等价 |
| BACK | onBackPressed 里用它判断 WebView 历史,先网页回退再退 App |
| MENU | App 级功能,单独拦截,弹出地址选择列表 |
不在白名单里的(音量、静音、电源、主页、数字键、媒体键...)一律不进 WebView,系统该怎么响应就怎么响应。
总结
- 黑名单累了维护人,白名单溜了漏网鱼
- WebView 只配拿到它需要的键
- 拦截放在 Activity 层,别在 WebView 里搞