TV アプリの場合、ブラウジング エクスペリエンスは効率的なフォーカス ベースのナビゲーションに依存します。標準の Compose Foundation 遅延レイアウトを使用すると、フォーカス駆動型のスクロールを自動的に処理してアクティブなアイテムを表示し続ける、パフォーマンスの高い縦方向と横方向のリストを作成できます。
テレビ向けに最適化されたデフォルトのスクロール動作
Compose Foundation 1.7.0 以降、標準の遅延レイアウト(LazyRow や LazyColumn など)には、フォーカス位置指定機能の組み込みサポートが含まれています。これは、フォーカスされたアイテムをユーザーに表示し、直感的に配置できるため、TV アプリのカタログを作成する際におすすめの方法です。
基本的なスクロール可能なリストを実装するには、標準の遅延コンポーネントを使用します。これらのコンポーネントは、D-pad ナビゲーションを自動的に処理し、フォーカスされたアイテムを表示します。
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items @Composable fun MovieCatalog(movies: List<Movie>) { LazyRow { items(movies) { movie -> MovieCard( movie = movie, onClick = { /* Handle click */ } ) } } }
BringIntoViewSpec でスクロール動作をカスタマイズする
デザインで特定の「ピボット」ポイントが必要な場合(たとえば、フォーカスされたアイテムを左端から正確に 30% の位置に維持する場合)、BringIntoViewSpec を使用してスクロール動作をカスタマイズできます。これは、フォーカスされたアイテムに合わせてビューポートをスクロールする方法を正確に定義できるようにすることで、以前の pivotOffsets 機能を置き換えるものです。
1. カスタム BringIntoViewSpec を定義する
次のヘルパー コンポーザブルを使用すると、親と子の割合に基づいて「ピボット」を定義できます。parentFraction は、アイテムをコンテナ内のどこに配置するかを指定します。childFraction は、アイテムのどの部分をそのポイントに合わせるかを指定します。
@OptIn(ExperimentalFoundationApi::class) @Composable fun PositionFocusedItemInLazyLayout( parentFraction: Float = 0.3f, childFraction: Float = 0f, content: @Composable () -> Unit, ) { val bringIntoViewSpec = remember(parentFraction, childFraction) { object : BringIntoViewSpec { override fun calculateScrollDistance( offset: Float, // Item's initial position size: Float, // Item's size containerSize: Float // Container's size ): Float { // Calculate the offset position of the item's leading edge. val initialTargetForLeadingEdge = parentFraction * containerSize - (childFraction * size) // If the item fits in the container, and scrolling would cause // its trailing edge to be clipped, adjust targetForLeadingEdge // to prevent over-scrolling near the end of list. val targetForLeadingEdge = if (size <= containerSize && (containerSize - initialTargetForLeadingEdge) < size) { // If clipped, align the item's trailing edge with the // container's trailing edge. containerSize - size } else { initialTargetForLeadingEdge } // Return scroll distance relative to initial item position. return offset - targetForLeadingEdge } } } // Apply the spec to all scrollables in the hierarchy CompositionLocalProvider( LocalBringIntoViewSpec provides bringIntoViewSpec, content = content, ) }
2. カスタム仕様を適用する
レイアウトをヘルパーでラップして、位置を適用します。これは、カタログのさまざまな行に「一貫したフォーカス ライン」を作成する場合に便利です。
PositionFocusedItemInLazyLayout( parentFraction = 0.3f, // Pivot 30% from the edge childFraction = 0.5f // Center of the item aligns with the pivot ) { LazyColumn { items(sectionList) { section -> // This row and its items will respect the 30% pivot LazyRow { /* ... */ } } } }
3. 特定のネストされたレイアウトをオプトアウトする
カスタム ピボットではなく標準のスクロール動作を使用する必要がある特定のネストされたレイアウトがある場合は、DefaultBringIntoViewSpec を指定します。
private val DefaultBringIntoViewSpec = object : BringIntoViewSpec {} PositionFocusedItemInLazyLayout { LazyColumn { item { // This row will ignore the custom pivot and use default behavior CompositionLocalProvider(LocalBringIntoViewSpec provides DefaultBringIntoViewSpec) { LazyRow { /* ... */ } } } } }
実際には、空の BringIntoViewSpec を渡すことで、フレームワークのデフォルトの動作が引き継がれます。
TV Foundation から Compose Foundation への移行
androidx.tv.foundation の TV 固有の遅延レイアウトは非推奨となり、標準の Compose Foundation レイアウトが推奨されます。
依存関係の更新
build.gradle で次のバージョン 1.7.0 以降が使用されていることを確認します。
androidx.compose.foundationandroidx.compose.runtime
コンポーネントのマッピング
移行するには、インポートを更新し、コンポーネントから Tv 接頭辞を削除します。
| サポートが終了したテレビ コンポーネント | Compose Foundation の置き換え |
|---|---|
| TvLazyRow | LazyRow |
| TvLazyColumn | LazyColumn |
| TvLazyHorizontalGrid | LazyHorizontalGrid |
| TvLazyVerticalGrid | LazyVerticalGrid |
| pivotOffsets | BringIntoViewSpec(LocalBringIntoViewSpec 経由) |