AndroidのRecyclerViewでフォーカスを中央にキープする


AndroidのRecyclerViewでフォーカスを中央にキープする必要があり、ググったのですが日本語の記事がなかったので、記事にします。

やりたいこと

メニュータブを表示する際に、フォーカスを中央にキープする。
※menu1フォーカス中もmenu2フォーカス中もセンターにキープする。
 

ポイントは3つ

  • HorizontalGridViewを使う。(縦の場合はVerticalGridView)
  • windowAlignmentをWINDOW_ALIGN_NO_EDGEに設定する。
  • LayoutManagerを設定しない

実装内容

上記の画像のサンプルでは、ActivityにHorizontalGridViewを表示させています。
※HorizontalGridViewを使うには、Leanbackを依存関係に追加する必要があります。
 https://developer.android.com/jetpack/androidx/releases/leanback?hl=ja#declaring_dependencies

MenuActivity.kt
class MenuActivity : AppCompatActivity() {
    private val viewAdapter = MenuAdapter(arrayOf("menu 1", "menu 2", "menu 3", "menu 4", "menu 5"))

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_menu)
        // HorizontalGridViewを使用する
        // LayoutManagerを設定しない
        findViewById<HorizontalGridView>(R.id.recycler_view).apply {
            adapter = viewAdapter
            // windowAlignmentにWINDOW_ALIGN_NO_EDGEを設定
            windowAlignment = WINDOW_ALIGN_NO_EDGE
        }
    }
}
activity_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:context="com.ykato.sample.kotlin.MenuActivity">

    <androidx.leanback.widget.HorizontalGridView
        android:id="@+id/recycler_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:ignore="MissingConstraints">
        <requestFocus/>
    </androidx.leanback.widget.HorizontalGridView>
</androidx.constraintlayout.widget.ConstraintLayout>

RecyclerViewに設定する、AdapterとViewHolderは下記の通り。

MenuAdapter.kt
class MenuAdapter(private val data: Array<String>) :
        RecyclerView.Adapter<MenuAdapter.MenuViewHolder>() {

    class MenuViewHolder(val view: View) : RecyclerView.ViewHolder(view)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MenuViewHolder {
        val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.sample_text_view, parent, false)
        return MenuViewHolder(view)
    }

    override fun onBindViewHolder(holder: MenuViewHolder, position: Int) {
        holder.view.menu_text.text = data[position]
    }

    override fun getItemCount() = data.size
}
sample_text_view
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/menu_text"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:focusable="true"
        android:gravity="center"
        android:textSize="24dp"
        android:background="#AAAAAA"
        tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

参考