В приложениях для телевизоров удобство просмотра зависит от эффективной навигации на основе фокуса. Используя стандартные ленивые макеты Compose Foundation, вы можете создавать высокопроизводительные вертикальные и горизонтальные списки, которые автоматически обрабатывают прокрутку, управляемую фокусом, чтобы активные элементы оставались в поле зрения.
Поведение прокрутки по умолчанию оптимизировано для телевизоров.
Начиная с Compose Foundation 1.7.0, стандартные ленивые макеты (такие как LazyRow и LazyColumn ) включают встроенную поддержку функций позиционирования фокуса. Это рекомендуемый способ создания каталогов для приложений на ТВ, поскольку он помогает удерживать сфокусированные элементы видимыми и интуитивно понятным для пользователя расположением.
Для реализации простого прокручиваемого списка используйте стандартные ленивые компоненты. Эти компоненты автоматически обрабатывают навигацию с помощью 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. Отказ от использования определенных вложенных макетов.
Если у вас есть определённый вложенный макет, который должен использовать стандартное поведение прокрутки вместо вашего пользовательского элемента Pivot, укажите 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.foundation -
androidx.compose.runtime
Сопоставление компонентов
Для миграции обновите импорт и удалите префикс Tv из ваших компонентов:
| Устаревший телевизионный компонент | Замена фундамента |
|---|---|
| TvLazyRow | LazyRow |
| TvLazyColumn | LazyColumn |
| TvLazyHorizontalGrid | LazyHorizontalGrid |
| TvLazyVerticalGrid | LazyVerticalGrid |
| pivotOffsets | BringIntoViewSpec (через LocalBringIntoViewSpec) |