Jetpack Compose 1.4.0 (本記事執筆時点ではalpha05)で、HorizontalPager
とVerticalPager
が追加されました。これまでAccompanistで提供されていたものが、本家に取り込まれた形です。現時点ではまだExperimentalですが、今後は徐々に使われていくようになると思われます。
さて、これらのPagerの状態管理を担うPagerState
クラスには、currentPage
、settledPage
、targetPage
というページ番号を表すプロパティが3つあります。currentPage
はAccompanistにも存在しましたが、その他の2つはJetpack Composeで追加されました。これらのプロパティがどんな挙動をするのか、リファレンスを読んだだけではイメージがつかめなかったので、実際にサンプルを動かして試してみました。
サンプル
サンプルコードは下記のとおりです。映像はこれらの値を確認しやすいように、スロー再生にしています。
HorizontalPagerの上にcurrentPage
、settledPage
、targetPage
を表示しています。ページが切り替わると値が変化しますが、変化するタイミングや値がそれぞれ異なることが分かります。
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PagerSample() {
Column {
val pagerState = rememberPagerState()
Text(text = "currentPage = ${pagerState.currentPage}")
Text(text = "settledPage = ${pagerState.settledPage}")
Text(text = "targetPage = ${pagerState.targetPage}")
HorizontalPager(
pageCount = 3,
state = pagerState,
) { page ->
Text(
text = "Page $page",
style = MaterialTheme.typography.displayLarge,
modifier = Modifier
.fillMaxSize()
.border(width = 2.dp, color = MaterialTheme.colorScheme.onPrimaryContainer)
)
}
}
}
currentPage
currentPage
は、画面の中心に現在表示されているページです。ページ遷移中は、画面のちょうど半分までスクロールした時にcurrentPage
の値が切り替わります。
APIリファレンスには下記のように書かれています。
The page that sits closest to the snapped position. This is an observable value and will change as the pager scrolls either by gesture or animation.(スナップされた位置に最も近いページ。これは観測可能な値であり、ジェスチャーまたはアニメーションによってページャーがスクロールするにつれて変化します。※DeepL訳)
https://developer.android.com/reference/kotlin/androidx/compose/foundation/pager/PagerState#currentPage()
currentPage
はスクロール中も位置に合わせて値が切り替わるので、ページ遷移の効果を強調させるような用途で使うのに向いているプロパティと言えそうです。次の例は、currentPage
を水色にしています。このような実装をすると、ページが切り替わったことを強調し、新しいページに目線を向けさせる効果が期待できそうです。
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PagerSample() {
Column {
val pagerState = rememberPagerState()
HorizontalPager(
pageCount = 3,
state = pagerState,
) { page ->
val bgColor = if (page == pagerState.currentPage) Color.Cyan else Color.White
Text(
text = "Page $page",
style = MaterialTheme.typography.displayLarge,
modifier = Modifier
.fillMaxSize()
.background(bgColor)
)
}
}
}
settledPage
settledPage
も現在表示中のページを示しますが、こちらはスクロール中には値は更新されず、スクロール完了して画面が静止してから値が更新されます。”settled”という単語が耳慣れないですが、「確定した」などの意味を持っています。
APIリファレンスには下記のように書かれています。
The page that is currently “settled”. This is an animation/gesture unaware page in the sense that it will not be updated while the pages are being scrolled, but rather when the animation/scroll settles.(現在「確定」しているページです。これは、ページをスクロールしている間は更新されず、アニメーションやスクロールが落ち着いたときに更新されるという意味で、アニメーションやジェスチャーを意識していないページと言えます。※DeepL訳を一部改)
https://developer.android.com/reference/kotlin/androidx/compose/foundation/pager/PagerState#settledPage()
スクロールが完了したタイミングで値が変わるので、動画やアニメーションなどの動的なコンテンツを、スクロールが止まったタイミングでスタートする、といった用途に使えそうです。次の例では画面の文字をアニメーションさせています。最初に表示されているページのアニメーションは、そのページが見えなくなるまで継続しています。そして、スクロールが止まったタイミングで新しいページのアニメーションが開始しています。
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PagerSample() {
Column {
val pagerState = rememberPagerState()
HorizontalPager(
pageCount = 3,
state = pagerState,
pageSpacing = 10.dp,
modifier = Modifier.background(Color.LightGray)
) { page ->
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
.background(Color.White)
) {
if (page == pagerState.settledPage) {
val infiniteTransition = rememberInfiniteTransition()
val rotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(tween(3000, easing = LinearEasing))
)
Text(
text = "Page$page Playing!",
style = MaterialTheme.typography.displayLarge,
modifier = Modifier.graphicsLayer { rotationZ = rotation }
)
} else {
Text(
text = "Page$page Stopped",
style = MaterialTheme.typography.displayLarge,
)
}
}
}
}
}
targetPage
targetPage
は、スクロール中に意味を持つプロパティです。現在進行中のスクロールアニメーションが完了した時、どのページが表示された状態で止まるかを示します。最初の動画を見ていただくと分かりますが、必ずしも隣のページを示しているとは限りません。スワイプジェスチャーの速度などの条件によっては、2つ隣のページを示す場合もあり、スクロール中にも値が変わります。
APIリファレンスには下記のように書かれています。
The page this
https://developer.android.com/reference/kotlin/androidx/compose/foundation/pager/PagerState#targetPage()Pager
intends to settle to. During fling or animated scroll (fromanimateScrollToPage
this will represent the page this pager intends to settle to. When no scroll is ongoing, this will be equal tocurrentPage
.(このページャーが移動しようとするページ。フライングまたはアニメーションスクロールの間(animateScrollToPageから)、これはこのページャーが落ち着く予定のページを表します。スクロールが進行していないとき、これはcurrentPageと同じになります。※DeepL訳)
targetPage
は、ちょっと具体的な使い道が思いつきません。もしかすると、これから表示される予定のページのデータのロードを前もって開始する、などの目的で使えるかもなーと思ったりしましたが、スクロール中も値がころころ切り替わるので実装は複雑になりそうです。
以上、Jetpack Compose本家のPagerのcurrentPage
、settledPage
、targetPage
についてでした。まだExperimeltalということで今後変わる可能性もある点はご留意ください。