When we develop android TV application, we need to use remote control to control the focus of RecyclerView to show the user which item is currently selected. Inevitably, the following issues will be involved:
- Set up the
item
The effect of getting focus RecyclerView
Once you regain focus, select the last oneitem
RecyclerView
When you lose focus, keep ititem
Check effect of
Let’s tackle these three problems one by one
Set up theitem
The effect of getting focus
Let’s start with the renderings
Just set up the item layout like this,
- use
selector
Set the background - Set up the
clickable
andfocusable
fortrue
<! --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>
Copy the code
item_selector.xml
<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>
Copy the code
Once you regain focus, select the last oneitem
Let’s start with the renderings
The above effect can be achieved by using the VerticalGridView in the Leanback library.
Because VerticalGridView extends BaseGridView extends RecyclerView, the code that used to use RecyclerView basically doesn’t change and doesn’t call setLayoutManager.
Rely on
Implementation "androidx leanback: leanback: 1.0.0."
use
<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" />
Copy the code
When you lose focus, keep ititem
Check effect of
Let’s start with the renderings
When the focus switches between items,
-
Item0 goes to item1, item0 loses focus, item1 gets focus
-
From item1 to item2, item1 loses focus, item2 gains focus
If the focus changes from RecyclerView to another control, Item2 loses focus.
So we record the item that gets focus and loses focus. If the item that gets focus and loses focus is the same item, then RecyclerView loses focus. We need to set this item as selected effect.
class MainAdapter() : ListAdapter<String, RecyclerView.ViewHolder>(MainDiffCallback()) {
/** Records items that get focus and items that lose focus
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]) {
// The same item gets focus and loses focus. There are two situations:
// RecyclerView loses focus
// RecyclerView regaining focus
// Keep this item selected,
view.isSelected = true
lastSelectedView = view
} else{ lastSelectedView? .isSelected =false}}}fun bind(item: String) {
binding.apply {
this.item = item
executePendingBindings()
}
}
}
}
Copy the code
Complete demo
TvRecyclerViewDemo in github