前人未踏の領域へ 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

Jetpack Compose: Buttonを表示しようとすると落ちる

課題

Jetpack Composeでボタンを表示しようとしたが以下のエラーになってしまい表示できない。

NoSuchMethodError: No static method clickable

Stacktraceはコピーするの忘れたのでありません。

対応

composeのバージョンが古い場合に発生。
自分の場合、1.0.0-alpha09 でエラーになった。1.0.0-alpha10 にしたら解消された。

Jetpack Compose: widthとpaddingの関係について

Jetpack Compseで Compsableにpaddingの挙動が気になったのでちょっと確認してみた。

@Preview(name = "PaddingとWidthの確認", showBackground = true)
@Composable
fun BoxPreview() {
    Surface(
        modifier = Modifier
            .padding(20.dp)
            .width(200.dp)
            .height(200.dp), color = Color.LightGray
    ) {
        Card(
            modifier = Modifier
                .padding(20.dp)
                .fillMaxWidth(1F)
                .fillMaxHeight(1F), backgroundColor = Color.Cyan
        ) {

        }
    }
}

するとこうなる。罫線は10dpごとについている

f:id:takeR:20210123034652p:plain

  • paddingは指定したComposableの子要素ではなく、Composable自身に適用される
  • paddingはwitdh, heightで指定した箇所の外に適用される(上記の例では計20dp + 200dp + 20dp = 240dp使う)

PaddingがComposable自身に適用されるのに違和感がある。 親や子のComposableの設定にも影響を受けるかもしれないけどそっちの確認はまた今度。