リストを使用すると、Wear OS デバイス上でユーザーが選択肢の中からアイテムを選択できるようになります。
多くの Wear OS デバイスは円形の画面であるため、画面の上部や下部付近に表示されるリストアイテムが見づらくなっています。このため、Wear OS 向け Compose には、スケーリングおよびモーフィング アニメーションをサポートする TransformingLazyColumn という LazyColumn クラスのバージョンが含まれています。アイテムが端に移動すると、アイテムは小さくなり、フェードアウトします。
推奨される拡大縮小とスクロールの効果を適用するには:
Modifier.transformedHeightを使用すると、Compose でアイテムが画面をスクロールする際の高さの変化を計算できます。transformation = SurfaceTransformation(transformationSpec)を使用して、アイテムのコンテンツの縮小などの視覚効果を適用します。transformationをパラメータとして受け取らないコンポーネント(Textなど)には、カスタムのTransformationSpecを使用します。
次のアニメーションは、リスト要素が画面の上部と下部に近づくにつれて、どのように拡大縮小され、形状が変化するかを示しています。
次のコード スニペットは、TransformingLazyColumn レイアウトを使用して、さまざまな Wear OS 画面サイズで美しく表示されるコンテンツを作成する方法を示しています。
このスニペットでは、リストの上下に適切なパディングを適用するために、リスト項目に設定する必要がある minimumVerticalContentPadding 修飾子の使用方法も示しています。
スクロール インジケーターを表示するには、ScreenScaffold と TransformingLazyColumn の間で columnState を共有します。
val columnState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold( scrollState = columnState ) { contentPadding -> TransformingLazyColumn( state = columnState, contentPadding = contentPadding ) { item { ListHeader( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) { Text(text = "Header") } } // ... other items item { Button( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding), transformation = SurfaceTransformation(transformationSpec), onClick = { /* ... */ }, icon = { Icon( imageVector = Icons.Default.Build, contentDescription = "build", ) }, ) { Text( text = "Build", maxLines = 1, overflow = TextOverflow.Ellipsis, ) } } } }
スナップ&フリング エフェクトを追加する
スナップとは、ユーザーがスクロールまたはフリングの操作を完了したときに、リストが特定のポイント(通常は画面の中央)に正確に配置された状態で落ち着くようにするものです。丸い画面では、アイテムが中央から離れるにつれて拡大縮小や変形が行われるため、スナップは、最も関連性の高いアイテムが最適な表示領域で完全に表示され、読み取り可能であることを保証するうえで特に便利です。スナップとフリングの動作を追加するには、flingBehavior パラメータを TransformingLazyColumnDefaults.snapFlingBehavior(columnState) に設定します。物理クラウンまたはベゼルを使用する際に一貫したエクスペリエンスを実現するには、rotaryScrollableBehavior を一致するように設定し、RotaryScrollableDefaults.snapBehavior(columnState) を使用します。
val columnState = rememberTransformingLazyColumnState() ScreenScaffold(scrollState = columnState) { contentPadding -> TransformingLazyColumn( state = columnState, flingBehavior = TransformingLazyColumnDefaults.snapFlingBehavior(columnState), rotaryScrollableBehavior = RotaryScrollableDefaults.snapBehavior(columnState) ) { // ... // ... } }
反転レイアウト
デフォルトでは、スクロール可能なリストは上端に固定されます。ユーザーが標準リストの最下部までスクロールし、新しいアイテムが末尾に追加された場合、リストは現在のアイテムに対するユーザーのビューを維持します。たとえば、ユーザーが画面の下部にあるアイテム 10 を表示しているときにアイテム 11 が追加されると、ビューはアイテム 10 にフォーカスされたままになり、アイテム 11 は現在のビューの下の画面外に表示されます。
メッセージ アプリケーションやライブログなどのユースケースでは、通常、この動作は望ましくありません。新しいアイテムが届いた場合、ユーザーは通常、リストの最下部にいる場合は最新のコンテンツをすぐに確認したいと考えます。一度に多くのアイテムが届いた場合、リストはスキップして一番下の最新のアイテムを表示します(つまり、ユーザーがスクロールして戻らない限り、中間アイテムはまったく表示されない可能性があります)。
これらのユースケースをサポートするため、TransformingLazyColumn では reverseLayout = true を設定してレイアウトを反転させることができます。これにより、リストのアンカーが上端から下端に変更されます。
便宜上、reverseLayout = true を設定すると、アイテムの視覚的な順序とスクロール ジェスチャーの方向も反転します。
- アイテムは下から上に構成されるため、インデックス 0 は画面の下部に表示されます。
- 上にスクロールすると、インデックスの高いアイテムが表示されます。
逆レイアウトとともにスナップとフリングの動作を追加するには、次のスニペットに示すように、flingBehavior と rotaryScrollableBehavior を組み合わせます。
val columnState = rememberTransformingLazyColumnState() val transformationSpec = rememberTransformationSpec() ScreenScaffold(scrollState = columnState) { contentPadding -> TransformingLazyColumn( state = columnState, contentPadding = contentPadding, reverseLayout = true, modifier = Modifier.fillMaxWidth() ) { items(10) { index -> Button( label = { Text( text = "Item ${index + 1}" ) }, onClick = {}, modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ButtonDefaults.minimumVerticalListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) } item { // With reverseLayout = true, the last item declared appears at the top. ListHeader( modifier = Modifier .fillMaxWidth() .transformedHeight(this, transformationSpec) .minimumVerticalContentPadding(ListHeaderDefaults.minimumTopListContentPadding), transformation = SurfaceTransformation(transformationSpec) ) { Text("Header") } } } }
次の画像は、通常のリストと反転リストの違いを示しています。

リストの端にあるボタン
マテリアル 3 では、リストの下部にエッジに沿ったボタンである EdgeButton を追加できます。ただし、これを TransformingLazyColumn 内のアイテムとして追加するのではなく、ScreenScaffold の edgeButton スロットを使用するようにしてください。
edgeButton スロットを使用すると、ボタンが画面の下部に正しく配置され、リストがスクロールされたときに適切に動作します。
次のコード スニペットは、EdgeButton の実装方法を示しています。
val columnState = rememberTransformingLazyColumnState() ScreenScaffold( scrollState = columnState, edgeButton = { EdgeButton( onClick = { /* TODO */ }, modifier = Modifier.scrollable( columnState, orientation = Orientation.Vertical, reverseDirection = true, // Apply overscroll to the EdgeButton for proper scrolling behavior. overscrollEffect = rememberOverscrollEffect(), ) ) { Text("More") } } ) { contentPadding -> TransformingLazyColumn( contentPadding = contentPadding, state = columnState, ) { // ... // ... } }
リストの SwipeToReveal
SwipeToReveal コンポーネントを使用すると、スワイプしてリストアイテムのアクション(Card や Chip など)にアクセスできます。通常、スワイプすると、横から 1 つまたは 2 つのアクション ボタン([削除] や [その他] など)が表示されます。
TransformingLazyColumn 内で SwipeToReveal を使用する場合は、次のガイドラインに沿ってください。
- スクロール時にリセット: ユーザーがリストをスクロールしたときに、スワイプして開いたアイテムを覆われた状態にリセットします。
- 高さの一貫性: 操作ボタンの高さを、内側のスワイプ可能なアイテム(
ButtonまたはCard)に合わせて設定し、一貫した外観を確保します。 - コンテナを変換する:
transformedHeight修飾子とtransformationSpecをSwipeToRevealコンポーネント自体に適用します。 - 二重変換しない: 内側のスワイプ可能なアイテム(
SwipeToRevealコンテナ内のカードまたはボタン)にtransformedHeight修飾子またはtransformation修飾子を適用しないでください。
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- Wear OS 向け Compose の Codelab
- リストとグリッド
- Compose でビューを使用する