Para apps de TV, a experiência de navegação depende de uma navegação eficiente baseada em foco. Usando layouts lentos padrão do Compose Foundation, é possível criar listas verticais e horizontais de alto desempenho que processam automaticamente a rolagem orientada por foco para manter os itens ativos visíveis.
Comportamento de rolagem padrão otimizado para TV
A partir do Compose Foundation 1.7.0, os layouts lentos padrão (como LazyRow
e LazyColumn) incluem suporte integrado para recursos de posicionamento de foco. Essa
é a maneira recomendada de criar catálogos para apps de TV, porque ajuda a manter os itens
em foco visíveis e posicionados de forma intuitiva para o usuário.
Para implementar uma lista rolável básica, use os componentes lentos padrão. Esses componentes processam automaticamente a navegação do botão direcional e mostram o item em foco.
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 */ } ) } } }
Personalizar o comportamento de rolagem com BringIntoViewSpec
Se o design exigir um ponto de "pivô" específico (por exemplo, manter o
item em foco exatamente a 30% da borda esquerda), personalize o comportamento de rolagem
usando um BringIntoViewSpec. Isso substitui a funcionalidade pivotOffsets
antiga, permitindo que você defina exatamente como a janela de visualização deve rolar
para acomodar um item em foco.
1. Definir um BringIntoViewSpec personalizado
O seguinte elemento combinável auxiliar permite definir um "pivô" com base nas frações pai e
filho. O parentFraction determina onde o item deve ficar no contêiner, e o childFraction determina qual parte do item se alinha com esse ponto.
@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. Aplicar a especificação personalizada
Envolva seus layouts com o auxiliar para aplicar o posicionamento. Isso é útil para criar uma "linha de foco consistente" em diferentes linhas do catálogo.
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. Desativar layouts aninhados específicos
Se você tiver um layout aninhado específico que precise usar o comportamento de rolagem padrão
em vez do pivô personalizado, forneça o 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 { /* ... */ } } } } }
Na verdade, ao transmitir um BringIntoViewSpec vazio, o comportamento padrão do framework é ativado.
Migração do TV Foundation para o Compose Foundation
Os layouts lentos específicos para TV em androidx.tv.foundation foram descontinuados em favor
dos layouts padrão do Compose Foundation.
Atualizações de dependência
Verifique se o build.gradle usa a versão 1.7.0 ou mais recente para:
androidx.compose.foundationandroidx.compose.runtime
Mapeamento de componentes
Para migrar, atualize as importações e remova o prefixo Tv dos componentes:
| Componente de TV descontinuado | Substituição do Compose Foundation |
|---|---|
| TvLazyRow | LazyRow |
| TvLazyColumn | LazyColumn |
| TvLazyHorizontalGrid | LazyHorizontalGrid |
| TvLazyVerticalGrid | LazyVerticalGrid |
| pivotOffsets | BringIntoViewSpec (via LocalBringIntoViewSpec) |