В ТВ-приложениях навигация основана на фокусе. Используя стандартные ленивые макеты 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
Следующая вспомогательная composable-функция позволяет определить "опорную точку" на основе долей родительского и дочернего элементов. Свойство 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) |