ViewPager2で要素をループさせる

ViewPager2で要素をループさせる方法の紹介です。いわゆる循環リストです。

ViewPager2ではRecyclerViewを使うことが出来るので、RecyclerViewと同じような実装で実現することが出来ます。

最終的にこんなのが作れます。(a、b、cの要素でループしている)

今回の検証に用いたコードは、satoshun/ViewPager2にあります😃

では、コードを説明していきます。今回は、1つのViewTypeを扱います。

実際のコード

まず、RecyclerView.Adapterのサイズを決めるgetItemCountメソッドの実装です。 限りなく大きい値、Int.MAX_VALUEを返します。

override fun getItemCount(): Int = Int.MAX_VALUE

次に、onBindViewHolderメソッドを次のように実装します。

private val itemData: List<Data>

override fun onBindViewHolder(holder: InfiniteViewHolder, position: Int) {
  val data = itemData[position % itemData.size]
  ...
}

itemDataにはViewの生成に必要な実際のデータが入っています。position % itemData.sizeとindexを取ることで、ループ中のどこの位置にいるかを特定することが出来ます。

最終的な、RecyclerView.Adapterは次のようになります。(onCreateViewHolderメソッドは重要でないので、省略しています)

class InfiniteAdapter(
  private val itemData: List<Data>
) : RecyclerView.Adapter<InfiniteViewHolder>() {

  override fun getItemCount(): Int = Int.MAX_VALUE

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ...

  override fun onBindViewHolder(holder: InfiniteViewHolder, position: Int) {
    val data = itemData[position % itemData.size]
    ...
  }
}

最後に、ViewPager2のセットアップをします。

with(binding.viewpager) {
  val infiniteAdapter = InfiniteAdapter(itemData = mockAdapterData)
  adapter = infiniteAdapter
  val center = Int.MAX_VALUE / 2
  val start = center - (center % mockAdapterData.size)
  setCurrentItem(start, false)
}

private val mockAdapterData = (0..2).map {
  Data(title = ('a' + it).toString())
}

setCurrentItemには、中心をセットしてあげます。そうすることで、初期状態でも左にスクロールすることが出来ます。

これで完成です!複数のViewTypeを扱いたいときは、もう少し工夫が必要になると思いますが、基本はこれだけで動かすことが出来ます。

注意点

まとめ

Written by
あんどろいどでぃべろっぱぁー🍎