はじめに
皆さん、ごきげんよう!れぶです!
今回の記事では、ViewPager2
×FragmentStateAdapter
で無限ループさせる方法をまとめます。
Fragment
間の無限スクロール動作を実現したい方に特に参考になればと思います。それでは、参りましょう!!
開発環境
- MacBook Air
- Android Studio Dolphin | 2021.3.1
- Kotlin
- compileSdkVersion 33
- targetSdkVersion 33
- minSdkVersion 21
動作イメージ
今回は以下のように、縦方向のスクロールで3ページ分を無限ループします。
考え方
結論、実際のページに偽の2ページ分を追加するやり方です。あくまで実際の最初or最後のページから移動したかを判定するために行います。
-
実際の最初のページ(
1
)の前に、偽の最後のページ(FAKE3
)を配置します。これによって、偽の最後のページ(FAKE3
)に移動したと同時に、実際の最後のページ(3
)にアニメーションなしで見た目上バレずに移動させます。 -
実際の最後のページ(
3
)の後に、偽の最初のページ(FAKE1
)を配置します。これによって、偽の最初のページ(FAKE1
)に移動したと同時に、実際の最初のページ(1
)にアニメーションなしで見た目上バレずに移動させます。
解説
ViewPager2と接続するAdapter
側と、ViewPager2を表示するView
側に実装します。
Adapter側の実装
class ViewPagerAdapter(mainActivity: MainActivity): FragmentStateAdapter(mainActivity) {
private val realNumPage = 3
override fun getItemCount(): Int = realNumPage + 2
fun getRealCount() = realNumPage
override fun createFragment(position: Int): Fragment {
return when(position) {
0,3 -> ThirdFragment()
1,4 -> FirstFragment()
2 -> SecondFragment()
else -> ThirdFragment()
}
}
}
ViewPager2で読み込むページ数は、実際のページ数+2
に設定します。
あとは、位置がずれないようにFragment
を配置すればOKです。
View側の実装
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//ViewPager2の設定
viewPager2 = findViewById(R.id.viewPager2)
viewPagerAdapter = ViewPagerAdapter(this)
viewPager2.apply {
adapter = viewPagerAdapter
setCurrentItem(1,false)
offscreenPageLimit = 1
registerOnPageChangeCallback(callBack)
}
}
private val callBack = object : OnPageChangeCallback() {
private var realPosition = -1
override fun onPageScrollStateChanged(state: Int) {
super.onPageScrollStateChanged(state)
//最初と最終ページの場合、無限ループになるように該当ページへ移動
if (state == ViewPager2.SCROLL_STATE_IDLE && realPosition >= 0) {
viewPager2.setCurrentItem(realPosition, false)
realPosition = -1
}
}
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
//最初と最終ページの場合、実際の位置を変数realPositionに代入
when (position) {
0 -> realPosition = viewPagerAdapter.getRealCount()
viewPagerAdapter.getRealCount() + 1 -> realPosition = 1
}
}
}
肝は、ViewPager2にコールバックを追加し、ページ状況を逐一管理してあげることです。
onPageSelected()
で、最初or最後のページが選択された場合に、それぞれ移動する位置を変数に入れます。onPageScrollStateChanged()
で、最初or最後のページに到達した場合に、その変数の位置にアニメーションなしで見た目上バレずに移動させます。彼らはあくまで偽物で、実際の表示用ではないためです。
ViewPager2の初回読み込み時に、setCurrentItem()
で1番目の位置に設定することを忘れずに行えば完了です。
サンプルコード
補足
実際の最初or最後のページで動画等の重め処理を行う際は、それらのページが一瞬固まることもあります。その場合は以下二つで回避できます。
-
offscreenPageLimit
で、ViewPager2で一度に読み込むページ数の設定を変更する - ViewPager2に配置する
Fragment
の順番を変更する
実務で発見しました。以上余談でした。
おわりに
今回はViewPager2
で無限スクロールを実現する方法を整理しました。実際の最初or最後のページから移動したかを判定するためだけに偽の2ページを両端に追加するという考え方が理解できれば、容易に実装できるのではないでしょうか。
ユーザのスクロール操作の負担を少しでも軽くするために、この記事が少しでも役立つと嬉しいです。以上です。ありがとうございました!