В приложениях для телевизоров навигация осуществляется с помощью фокуса. Используя стандартные ленивые макеты 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 replacement |
|---|---|
| TvLazyRow | LazyRow |
| TvLazyColumn | LazyColumn |
| TvLazyHorizontalGrid | LazyHorizontalGrid |
| TvLazyVerticalGrid | LazyVerticalGrid |
| pivotOffsets | BringIntoViewSpec (через LocalBringIntoViewSpec) |