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 eficientes que processam automaticamente a rolagem orientada por foco para manter os itens ativos em exibição.
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 lazy padrão. Esses componentes processam automaticamente a navegação com o botão direcional e trazem o item em foco para a tela.
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 30% da borda esquerda), personalize o comportamento de rolagem
usando um BringIntoViewSpec. Isso substitui a funcionalidade pivotOffsets
mais 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 elemento combinável auxiliar a seguir permite definir um "pivô" com base em frações de elementos pai e filho. O parentFraction determina onde no contêiner o item
deve ficar, 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
Encapsule seus layouts com o auxiliar para aplicar o posicionamento. Isso é útil para criar uma "linha de foco consistente" em diferentes linhas do seu 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 seu 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 prática, transmitir um BringIntoViewSpec vazio permite que o comportamento padrão do framework assuma o controle.
Migração da TV Foundation para a 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ências
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) |