前人未踏の領域へ Androidアプリ開発編

Androidアプリ開発に関する調査メモ置き場。古い記事にはアプリ以外も含まれます。

ViewPager2で操作性が悪化した件

FragmentStatePagerAdapterがdeprecatedになったのでViewPager2に移行作業中。

新しいFragmentStateAdapterからRecyclerView.Adapterを継承するようになったのだけど、 そのせいかPageコンテンツである子Fragmentの縦スクロール中に横スワイプが反応しやすくなっている。つまり操作性が悪くなっている。

フリックならいいんだけど、指を離さずに上下スクロールをしようとするとどうしてもX軸もずれてしまい、そうするとViewPager側が出しゃばってくるのだ。 こういう話題ってエラーとかと違って英語で検索しにくいから話題になってるかどうかも良く分からないのだけど(Sensitiveという指摘は散見)、Swipeをdisabledにする方法を質問している人がいたので多分縦スクロール発動中は disabledにするような対応がいいんだろうな...って結構面倒なんだけど!

対策

  • ViewPager2を持つFragmentのViewModelにisChildScrolling的なLiveDataを用意
  • 子Fragmentのスクロールが開始(newState == `SCROLL_STATE_DRAGGING )したらisChildScrollingをtrueにする
  • isChildScrollingをobserveしてtrueになったらsetUserInputEnabledをfalseにする
  • スクロールが止まったらtrueに戻す。
// ViewModel

/**
 * 子Fragmentがスクロール中かどうか
 */
private val _isChildScrolling: MutableLiveData<Boolean> by lazy {
    MutableLiveData<Boolean>()
}
val isChildScrolling: LiveData<Boolean>
    get() = _isChildScrolling

fun setIsChildScrolling(b: Boolean) {
    _isChildScrolling = b
}
// addOnScrollListenerの一部

gridView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
      override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
          super.onScrollStateChanged(recyclerView, newState)

          viewModel.setIsChildScrolling( newState == RecyclerView.SCROLL_STATE_DRAGGING )
      }
}
// observer

viewModel.isChildScrolling.observe(this) {
    pagerBinding.viewPager.isUserInputEnabled = !it
}

雑感

この対策すると上下にスクロールさせながらスワイプさせてみたいなグリグリした操作感はなくなる。 ただ意図せず横スワイプに操作を持っていかれるくらいならこの方が良いだろう。

参考

stackoverflow.com

敏感過ぎるよ!って投稿もあった。 stackoverflow.com