🔖

HorizontalPagerの中にLazyRowを置いたときにpagerのスクロールを無効にする方法

2022/07/23に公開

HorizontalPagerとLazyRowのスクロールが競合してしまう問題点

LazyRowの末端までスクロールした時に親のHorizontalPagerのスクロールに移ってしまいpagerが動いてしまう問題がありました。
要件的にpagerのスクロールを無効にしたいため解決策を調べました。

実装方法

依存関係のバージョンは以下です。

implementation 'com.google.accompanist:accompanist-pager:0.23.0'

Accompanistは、Jetpack Composeにはまだ実装されていない機能を補完したライブラリ群です。

PagerScreen.kt
HorizontalPager(
    state = pagerState,
    count = 3,
) {
    Column {
        Spacer(modifier = Modifier.height(300.dp))
        LazyRow {
            item {
                Spacer(modifier = Modifier.width(20.dp))
            }
            (1..10).forEach { index ->
                item {
                    Box(
                        modifier = Modifier
                            .width(160.dp)
                            .height(90.dp)
                            .background(Color.Gray),
                        contentAlignment = Alignment.Center
                    ) {
                        Text(text = index.toString())
                    }
                    Spacer(modifier = Modifier.width(20.dp))
                }
            }
        }
    }
}

原因

自動ネストスクロールによってスクロールアクションを開始する操作は、子から親に自動的に伝播されます。
つまり、子であるLazyRowのスクロールがそれ以上できなくなると、親であるHorizontalPagerのスクロールに移ってしまうことが原因でした。

参考:https://developer.android.com/jetpack/compose/gestures?hl=ja#auto-nested-scrolling

対応方法

LazyRowをBoxで囲いmodifierにスクロール可能にするscrollableと
特定方向のスワイプを無効にするdraggableを使うことによって解決できました。

PagerScreen.kt
HorizontalPager(
    state = pagerState,
    count = 3,
) {
    Column {
        Spacer(modifier = Modifier.height(300.dp))
+        Box(
+            modifier = Modifier
+                // LazyRowが末端までスクロール後、HorizontalPagerを動かないようにする
+                .scrollable(rememberScrollableState { it }, Orientation.Horizontal)
+                .draggable(
+                    interactionSource = remember { MutableInteractionSource() },
+                    state = remember { DraggableState {} },
+                    orientation = Orientation.Horizontal,
+                )
+        ) {
            LazyRow() {
                item {
                    Spacer(modifier = Modifier.width(20.dp))
                }
                (1..10).forEach { index ->
                    item {
                        Box(
                            modifier = Modifier
                                .width(160.dp)
                                .height(90.dp)
                                .background(Color.Gray),
                            contentAlignment = Alignment.Center
                        ) {
                            Text(text = index.toString())
                        }
                        Spacer(modifier = Modifier.width(20.dp))
                    }
                }
            }
+        }
    }
}

Discussion