テレビアプリの場合、ブラウジング エクスペリエンスは効率的なフォーカスベースのナビゲーションに依存します。標準の 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 のテレビ専用の遅延レイアウトは、標準の 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 経由) |