android:state_hovered在多数android移动设备上不生效,仅限外接鼠标或模拟悬停的chromebook/模拟器;应优先使用onhoverlistener并注意资源混淆保留。

Android selector 里 android:state_hovered 根本不生效?
绝大多数情况下,android:state_hovered 在真机上完全没反应——不是你写错了,是 Android 系统压根不给它机会。从 Android 4.0 到 14,只有极少数带真正触摸板或外接鼠标且开启“模拟悬停”的设备(比如部分 Chromebook 或调试中的模拟器)会触发该状态。普通手机、平板连触摸事件都走 state_pressed,根本不会进 state_hovered 分支。
实操建议:
- 别在面向移动设备的 UI 中依赖
android:state_hovered做核心交互反馈 - 如果必须支持鼠标悬停(比如折叠屏/大屏模式),先用
Configuration.uiMode判断是否处于UI_MODE_TYPE_DESK或UI_MODE_TYPE_CAR - 用
View.setOnHoverListener()手动监听MOTION_EVENT_ACTION_HOVER_ENTER/EXIT,比靠 selector 可靠得多
为什么 selector 里写了 state_hovered 却总匹配到 state_focused?
因为 Android 的状态匹配是「按顺序找第一个全匹配项」,而 state_focused 和 state_hovered 在多数输入场景下会同时为 true(比如鼠标移入一个可聚焦的 Button)。如果你把 state_focused 的 item 写在 state_hovered 前面,系统就永远选不到后者。
实操建议:
- 把最具体的组合放前面:比如
<item android:state_hovered="true" android:state_focused="false"></item>要比单独state_hovered更准 - 避免混用
state_focused和state_hovered在同一 selector 中,除非你明确控制焦点获取逻辑 - 用
adb shell dumpsys input_method或布局检查器确认当前 View 的实际状态值
替代方案:用 View.OnHoverListener 实现可靠悬停反馈
比起赌系统会不会发 state_hovered,直接监听更可控。它不依赖主题、不被 selector 顺序干扰,还能做渐变、延迟显示等原生 selector 做不了的事。
实操建议:
- 在
onCreate()或onViewCreated()中注册:view.setOnHoverListener((v, event) -> { ... }) - 只响应
event.getAction() == MotionEvent.ACTION_HOVER_ENTER和ACTION_HOVER_EXIT,忽略ACTION_HOVER_MOVE(太频繁) - 配合
ValueAnimator做颜色过渡,比 selector 切换更顺滑;注意在ACTION_HOVER_EXIT时取消动画防止内存泄漏 - 记得在
onDestroy()或onViewDetachedFromWindow()中设为null,避免隐式引用
混淆和构建时 state_hovered 属性被删掉?
AGP 8.0+ 默认启用资源压缩(shrinkResources true),而 android:state_hovered 是较冷门属性,R8 可能误判为未使用并移除对应资源 ID,导致运行时报 ResourceNotFoundException 或静默失效。
实操建议:
- 在
res/raw/keep.xml中显式保留:<resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@drawable/*,@color/*"></resources> - 或者在
proguard-rules.pro加一行:-keep class android.R$attr { public static int state_hovered; } - 验证方式:反编译 APK,搜
state_hovered是否还在resources.arsc里










