Para alternar o conteúdo horizontal ou verticalmente, use os elementos combináveis
HorizontalPager e VerticalPager. Eles têm
funções semelhantes ao ViewPager no sistema de visualização. Por padrão, HorizontalPager ocupa toda a largura da tela e VerticalPager ocupa toda a altura. Os paginadores também só exibem uma página por vez. Todos esses padrões são configuráveis.
HorizontalPager
Para criar um paginador que role horizontalmente para a esquerda e para a direita, use HorizontalPager:
HorizontalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
VerticalPager
Para criar um paginador que role para cima e para baixo, use VerticalPager:
VerticalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) VerticalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
Criação lenta
As páginas em HorizontalPager e VerticalPager são compostas lentamente
e dispostas quando necessário. À medida que o usuário rola as páginas, o elemento combinável remove as páginas que não são mais necessárias.
Carregar mais páginas fora da tela
Por padrão, o paginador só carrega as páginas visíveis na tela. Para carregar mais páginas fora da tela, defina beyondBoundsPageCount como um valor maior que zero.
Rolar até um item no paginador
Para rolar até uma determinada página no paginador, crie um PagerState objeto
usando rememberPagerState() e transmita-o como o parâmetro state para o
paginador. Você pode chamar PagerState#scrollToPage() nesse estado, dentro de um
CoroutineScope:
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.scrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
Se você quiser animar a página, use a
PagerState#animateScrollToPage() função:
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.animateScrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
Receber notificações sobre mudanças no estado da página
PagerState tem três propriedades com informações sobre páginas:
currentPage, settledPage e targetPage.
currentPage: a página mais próxima da posição de ajuste. Por padrão, a posição de ajuste está no início do layout.settledPage: o número da página quando nenhuma animação ou rolagem está em execução. Isso é diferente da propriedadecurrentPage, porquecurrentPageé atualizada imediatamente se a página estiver perto o suficiente da posição de ajuste, massettledPagepermanece a mesma até que todas as animações sejam concluídas.targetPage: a posição de parada proposta para um movimento de rolagem.
Você pode usar a função snapshotFlow para observar as mudanças nessas variáveis e reagir a elas. Por exemplo, para enviar um evento de análise em cada mudança de página, faça o seguinte:
val pagerState = rememberPagerState(pageCount = { 10 }) LaunchedEffect(pagerState) { // Collect from the a snapshotFlow reading the currentPage snapshotFlow { pagerState.currentPage }.collect { page -> // Do something with each page change, for example: // viewModel.sendPageSelectedEvent(page) Log.d("Page change", "Page changed to $page") } } VerticalPager( state = pagerState, ) { page -> Text(text = "Page: $page") }
Adicionar um indicador de página
Para adicionar um indicador a uma página, use o objeto PagerState para receber informações sobre qual página está selecionada no número de páginas e desenhe seu indicador personalizado.
Por exemplo, para criar um indicador de círculo, repita o número de círculos e mude a cor do círculo com base na seleção da página, usando pagerState.currentPage:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize() ) { page -> // Our page content Text( text = "Page: $page", ) } Row( Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pagerState.pageCount) { iteration -> val color = if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray Box( modifier = Modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } }
Aplicar efeitos de rolagem de itens ao conteúdo
Um caso de uso comum é usar a posição de rolagem para aplicar efeitos aos itens do paginador. Para saber a distância de uma página da página selecionada, use
PagerState.currentPageOffsetFraction. Em seguida, aplique efeitos de transformação ao conteúdo com base na distância da página selecionada.
Por exemplo, para ajustar a opacidade dos itens com base na distância do
centro, mude o alpha usando Modifier.graphicsLayer em um item
dentro do paginador:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager(state = pagerState) { page -> Card( Modifier .size(200.dp) .graphicsLayer { // Calculate the absolute offset for the current page from the // scroll position. We use the absolute value which allows us to mirror // any effects for both directions val pageOffset = ( (pagerState.currentPage - page) + pagerState .currentPageOffsetFraction ).absoluteValue // We animate the alpha, between 50% and 100% alpha = lerp( start = 0.5f, stop = 1f, fraction = 1f - pageOffset.coerceIn(0f, 1f) ) } ) { // Card content } }
Tamanhos de página personalizados
Por padrão, HorizontalPager e VerticalPager ocupam toda a largura ou altura, respectivamente. Você pode definir a variável pageSize para ter um
Fixed, Fill (padrão) ou um cálculo de tamanho personalizado.
Por exemplo, para definir uma página de largura fixa de 100.dp:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
Para dimensionar as páginas com base no tamanho da janela de visualização, use um cálculo de tamanho de página personalizado. Crie um objeto PageSize personalizado e divida o
availableSpace por três, considerando o espaçamento entre os itens:
private val threePagesPerViewport = object : PageSize { override fun Density.calculateMainAxisPageSize( availableSpace: Int, pageSpacing: Int ): Int { return (availableSpace - 2 * pageSpacing) / 3 } }
Padding de conteúdo
HorizontalPager e VerticalPager oferecem suporte à mudança do padding de conteúdo, o que permite influenciar o tamanho máximo e o alinhamento das páginas.
Por exemplo, definir o padding start alinha as páginas para o final:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(start = 64.dp), ) { page -> // page content }
Definir o padding start e end com o mesmo valor centraliza o item horizontalmente:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content }
Definir o padding end alinha as páginas para o início:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(end = 64.dp), ) { page -> // page content }
Você pode definir os valores top e bottom para conseguir efeitos semelhantes para VerticalPager. O valor 32.dp é usado apenas como exemplo. Você pode definir cada uma das dimensões de padding para qualquer valor.
Personalizar o comportamento de rolagem
Os elementos combináveis HorizontalPager e VerticalPager padrão especificam como os gestos de rolagem funcionam com o paginador. No entanto, você pode personalizar e mudar os padrões, como pagerSnapDistance ou flingBehavior.
Distância de ajuste
Por padrão, HorizontalPager e VerticalPager definem o número máximo de páginas que um gesto de deslizar rapidamente pode rolar para uma página por vez. Para mudar
isso, defina pagerSnapDistance no flingBehavior:
val pagerState = rememberPagerState(pageCount = { 10 }) val fling = PagerDefaults.flingBehavior( state = pagerState, pagerSnapDistance = PagerSnapDistance.atMost(10) ) Column(modifier = Modifier.fillMaxSize()) { HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(200.dp), beyondViewportPageCount = 10, flingBehavior = fling ) { PagerSampleItem(page = it) } }
Criar um paginador de avanço automático
Esta seção descreve como criar um paginador de avanço automático com indicadores de página no Compose. A coleção de itens rola automaticamente na horizontal, mas os usuários também podem deslizar manualmente entre os itens. Se um usuário interagir com o paginador, ele interromperá a progressão automática.
Exemplo básico
Juntos, os snippets a seguir criam uma implementação básica de paginador de avanço automático com um indicador visual, em que cada página é renderizada como uma cor diferente:
@Composable fun AutoAdvancePager(pageItems: List<Color>, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { val pagerState = rememberPagerState(pageCount = { pageItems.size }) val pagerIsDragged by pagerState.interactionSource.collectIsDraggedAsState() val pageInteractionSource = remember { MutableInteractionSource() } val pageIsPressed by pageInteractionSource.collectIsPressedAsState() // Stop auto-advancing when pager is dragged or one of the pages is pressed val autoAdvance = !pagerIsDragged && !pageIsPressed if (autoAdvance) { LaunchedEffect(pagerState, pageInteractionSource) { while (true) { delay(2000) val nextPage = (pagerState.currentPage + 1) % pageItems.size pagerState.animateScrollToPage(nextPage) } } } HorizontalPager( state = pagerState ) { page -> Text( text = "Page: $page", textAlign = TextAlign.Center, modifier = modifier .fillMaxSize() .background(pageItems[page]) .clickable( interactionSource = pageInteractionSource, indication = LocalIndication.current ) { // Handle page click } .wrapContentSize(align = Alignment.Center) ) } PagerIndicator(pageItems.size, pagerState.currentPage) } }
Principais pontos sobre o código
- A função
AutoAdvancePagercria uma visualização de paginação horizontal com avanço automático. Ela recebe uma lista de objetosColorcomo entrada, que são usados como cores de plano de fundo para cada página. pagerStateé criado usandorememberPagerState, que contém o estado do paginador.pagerIsDraggedepageIsPressedrastreiam a interação do usuário.- O
LaunchedEffectavança automaticamente o paginador a cada dois segundos, a menos que o usuário arraste o paginador ou pressione uma das páginas. HorizontalPagermostra uma lista de páginas, cada uma com um elemento combinávelTextmostrando o número da página. O modificador preenche a página, define a cor de plano de fundo depageItemse torna a página clicável.
@Composable fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pageCount) { iteration -> val color = if (currentPageIndex == iteration) Color.DarkGray else Color.LightGray Box( modifier = modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } } } }
Principais pontos sobre o código
- Um
Boxcombinável atua como o elemento raiz e contém umaRowpara organizar os indicadores de página horizontalmente. - Um indicador de página personalizado é mostrado como uma linha de círculos, em que cada
Boxrecortado em umCircleShaperepresenta uma página. - O círculo da página atual é colorido como
DarkGray, enquanto os outros círculos sãoLightGray. O parâmetrocurrentPageIndexdetermina qual círculo é renderizado em cinza-escuro.
Resultado
Este vídeo mostra o paginador de avanço automático básico dos snippets anteriores: