我们在开发android tv应用时需要使用遥控器来控制
- 设置
item 获得焦点时的效果 -
RecyclerView 重新获得焦点后,选中上次的item -
RecyclerView 失去焦点后,继续保持item 的选中效果
下面对这三个问题逐个击破
设置item 获得焦点时的效果
先上效果图
获得焦点.gif
只需要按下面这样设置
- 使用
selector 设置背景 - 设置
clickable 和focusable 为true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <!--item.xml--> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="60dp" android:background="@drawable/item_selector" android:clickable="true" android:focusable="true"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{item}" android:textColor="@android:color/white" android:textSize="24sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
1 2 3 4 5 6 | <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@color/select_bg_color" android:state_selected="true" /> <item android:drawable="@color/select_bg_color" android:state_focused="true" /> <item android:drawable="@android:color/darker_gray" /> </selector> |
重新获得焦点后,选中上次的item
先上效果图
选中上次位置.gif
上面的效果,我们使用
因为
依赖
使用
1 2 3 4 5 6 7 | <androidx.leanback.widget.VerticalGridView android:id="@+id/vertical_gridview" android:layout_width="100dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> |
失去焦点后,继续保持item 的选中效果
先上效果图
保持选中效果.gif
当焦点在
-
从
item0 到item1 ,item0 失去焦点,item1 获得焦点 -
从
item1 到item2 ,item1 失去焦点,item2 获得焦点
如果此时,焦点从
所以我们记录获得焦点和失去焦点的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | class MainAdapter() : ListAdapter<String, RecyclerView.ViewHolder>(MainDiffCallback()) { /** 记录获得焦点和失去焦点的item */ private val map = mutableMapOf<Boolean, String>() 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