Android TV--RecyclerView中item焦点实战

我们在开发android tv应用时需要使用遥控器来控制RecyclerView的焦点,来向用户展示当前选中的是哪个item。不可避免的会涉及以下几个问题:

  • 设置item获得焦点时的效果
  • RecyclerView重新获得焦点后,选中上次的item
  • RecyclerView失去焦点后,继续保持item的选中效果

下面对这三个问题逐个击破

设置item获得焦点时的效果

先上效果图

获得焦点.gif

只需要按下面这样设置item布局即可,

  • 使用selector设置背景
  • 设置clickablefocusabletrue



        

    

item_selector.xml



    
    
    

重新获得焦点后,选中上次的item

先上效果图

选中上次位置.gif

上面的效果,我们使用Leanback库中的VerticalGridView即可实现。

因为VerticalGridView extends BaseGridView extends RecyclerView,所以之前使用RecyclerView的代码基本不用改变,并且不用调用setLayoutManager

依赖

implementation "androidx.leanback:leanback:1.0.0"

使用


失去焦点后,继续保持item的选中效果

先上效果图

保持选中效果.gif

当焦点在item中切换时,

  • item0item1item0失去焦点,item1获得焦点

  • item1item2item1失去焦点,item2获得焦点

如果此时,焦点从RecyclerView切换到其他控件,item2失去焦点。

所以我们记录获得焦点和失去焦点的item,如果获得焦点和失去焦点的是同一个item,那么就表示RecyclerView失去了焦点,我们需要设置这个item为选中效果。

class MainAdapter() : ListAdapter(MainDiffCallback()) {

    /** 记录获得焦点和失去焦点的item */
    private val map = mutableMapOf()
    private var lastSelectedView: View? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return MainViewHolder(
                ItemBinding.inflate(
                        LayoutInflater.from(parent.context),
                        parent,
                        false
                )
        )
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val item = getItem(position)
        with(holder as MainViewHolder) {
            itemView.tag = item
            bind(item)
        }
    }

    inner class MainViewHolder(private val binding: ItemBinding) : RecyclerView.ViewHolder(binding.root) {
        init {
            binding.root.setOnFocusChangeListener { view, hasFocus ->
                map[hasFocus] = view.tag as String
                if (map[true] == map[false]) {
                    // 获得焦点和失去焦点的是同一个item,会有以下两种情况:
                    //  RecyclerView失去焦点
                    //  RecyclerView重新获得焦点
                    // 让此item保持选中状态,
                    view.isSelected = true
                    lastSelectedView = view
                } else {
                    lastSelectedView?.isSelected = false
                }
            }
        }


        fun bind(item: String) {
            binding.apply {
                this.item = item
                executePendingBindings()
            }
        }
    }
}

完整demo

TvRecyclerViewDemo in github

你可能感兴趣的:(Android TV--RecyclerView中item焦点实战)