我们在开发android tv应用时需要使用遥控器来控制RecyclerView
的焦点,来向用户展示当前选中的是哪个item
。不可避免的会涉及以下几个问题:
- 设置
item
获得焦点时的效果 -
RecyclerView
重新获得焦点后,选中上次的item
-
RecyclerView
失去焦点后,继续保持item
的选中效果
下面对这三个问题逐个击破
设置item
获得焦点时的效果
先上效果图
获得焦点.gif只需要按下面这样设置item
布局即可,
- 使用
selector
设置背景 - 设置
clickable
和focusable
为true
<!--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>
item_selector.xml
<?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上面的效果,我们使用Leanback
库中的VerticalGridView
即可实现。
因为VerticalGridView extends BaseGridView extends RecyclerView
,所以之前使用RecyclerView
的代码基本不用改变,并且不用调用setLayoutManager
。
依赖
implementation "androidx.leanback:leanback:1.0.0"
使用
<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当焦点在item
中切换时,
-
从
item0
到item1
,item0
失去焦点,item1
获得焦点 -
从
item1
到item2
,item1
失去焦点,item2
获得焦点
如果此时,焦点从RecyclerView
切换到其他控件,item2
失去焦点。
所以我们记录获得焦点和失去焦点的item
,如果获得焦点和失去焦点的是同一个item
,那么就表示RecyclerView
失去了焦点,我们需要设置这个item
为选中效果。
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()
}
}
}
}
网友评论