AndroidTV RecyclerView使用DPAD时如何监听焦点变化实现条目高亮
Android TV RecyclerView条目焦点变化监听实现方案
基础实现逻辑
RecyclerView本身未提供全局的条目焦点变化回调接口,最优实现方案是在ViewHolder初始化阶段为条目根布局绑定焦点监听器,性能损耗最低且逻辑最直接。
步骤1:确认条目布局基础配置
条目根布局需要先正确配置焦点相关属性:
- 同时设置
android:focusable="true"和android:focusableInTouchMode="true" - 若不需要子控件抢占焦点,可添加
android:descendantFocusability="blocksDescendants"属性,确保焦点直接落在条目根布局上
步骤2:在ViewHolder中绑定焦点监听器
在ViewHolder的初始化代码中给条目根布局设置OnFocusChangeListener,即可监听每个条目的焦点变化,同时通过adapterPosition识别对应条目:
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { init { itemView.setOnFocusChangeListener { view, hasFocus -> // 过滤ViewHolder未绑定到有效位置的异常场景 val position = adapterPosition if (position == RecyclerView.NO_POSITION) return@setOnFocusChangeListener if (hasFocus) { // 条目获得焦点,执行高亮动画,示例为放大效果 view.animate() .scaleX(1.1f) .scaleY(1.1f) .setDuration(200) .start() // 可通过position从数据源中获取当前焦点对应的条目数据 val focusItem = dataList[position] } else { // 条目失去焦点,执行恢复动画 view.animate() .scaleX(1.0f) .scaleY(1.0f) .setDuration(200) .start() } } } }
注意:不要在
onBindViewHolder方法中重复设置监听器,放在ViewHolder初始化逻辑中即可,避免重复创建监听实例造成性能浪费。
步骤3:可选:全局统一处理焦点逻辑
如果需要在Activity/Fragment层面统一处理焦点事件,可通过自定义回调接口将焦点事件向外抛出:
- 首先定义回调接口
interface OnItemFocusChangeListener { fun onItemFocusChanged(position: Int, hasFocus: Boolean, itemView: View) }
- 在Adapter构造函数中传入回调实例
class MyAdapter( private val dataList: List<YourDataType>, private val focusListener: OnItemFocusChangeListener ) : RecyclerView.Adapter<MyViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val itemView = LayoutInflater.from(parent.context) .inflate(R.layout.item_your_layout, parent, false) val holder = MyViewHolder(itemView) // 绑定焦点监听 itemView.setOnFocusChangeListener { view, hasFocus -> val pos = holder.adapterPosition if (pos != RecyclerView.NO_POSITION) { focusListener.onItemFocusChanged(pos, hasFocus, view) } } return holder } // 剩余Adapter逻辑省略 }
- 在Activity/Fragment中实现接口即可统一处理所有条目焦点逻辑。
常见问题排查
如果焦点监听器未触发,可按以下顺序排查:
- 确认条目根布局的
focusable和focusableInTouchMode属性均设置为true - 确认RecyclerView本身未设置
android:focusable="true",否则焦点会被RecyclerView本身拦截,不会下发到子条目 - 若条目内部存在可聚焦控件,可将子控件的
focusable属性设为false,或给条目根布局设置android:descendantFocusability="beforeDescendants"属性,确保根布局优先获得焦点
内容的提问来源于stack exchange,提问作者Harry Developer
相关产品推荐
相关产品推荐

