Pager no Compose

Para navegar pelo conteúdo para a esquerda e para a direita ou para cima e para baixo, use as HorizontalPager e VerticalPager elementos combináveis, respectivamente. Esses elementos combináveis têm funções semelhantes ViewPager na visualização sistema. Por padrão, HorizontalPager ocupa toda a largura da tela, O VerticalPager ocupa toda a altura, e os pagers apenas lançam uma página por vez. tempo de resposta. É possível configurar todos esses padrões.

HorizontalPager

Para criar um pager que role horizontalmente para a esquerda e para a direita, use HorizontalPager:

Figura 1. Demonstração de 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 pager que role para cima e para baixo, use VerticalPager:

Figura 2. Demonstração de 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 estão lentamente compostas e dispostas quando necessário. Como o usuário rola as páginas, o elemento combinável remove as páginas que não são mais obrigatórios.

Carregar mais páginas fora da tela

Por padrão, o pager carrega apenas 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 pager

Para rolar até uma determinada página no pager, crie uma PagerState usando rememberPagerState() e a transmita como o parâmetro state para o pager. Você pode ligar PagerState#scrollToPage() nesse estado, dentro de uma 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 o 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 alterações 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, o 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 propriedade currentPage, porque o currentPage atualiza imediatamente se a página estiver próxima o suficiente da posição do ajuste, mas O settledPage permanece o mesmo até que todas as animações terminem de ser executadas.
  • targetPage: a posição de parada proposta para um movimento de rolagem.

Use a função snapshotFlow para observar as mudanças dessas variáveis. e reagir a eles. Por exemplo, para enviar um evento de análise em cada alteração 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

Se quiser adicionar um indicador a uma página, use o objeto PagerState para receber informações sobre qual página foi selecionada entre o número de páginas e desenhar seu personalizado indicador.

Por exemplo, para um indicador de círculo simples, você pode repetir o número de círculos e altere 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)
        )
    }
}

Pager mostrando um indicador de círculo abaixo do conteúdo
Figura 3. Pager com um indicador de círculo abaixo do conteúdo

Aplicar efeitos de rolagem do item ao conteúdo

Um caso de uso comum é usar a posição de rolagem para aplicar efeitos ao pager. itens. Para descobrir a distância entre uma página e a selecionada atualmente, você pode usam PagerState.currentPageOffsetFraction Depois, aplique efeitos de transformação ao conteúdo com base na distância da página selecionada.

Figura 4. Como aplicar transformações ao conteúdo do pager

Por exemplo, para ajustar a opacidade de itens com base na distância entre eles center, mude o alpha usando Modifier.graphicsLayer em um item dentro do pager:

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 a largura total ou a altura total, respectivamente. É possível definir a variável pageSize para ter Fixed, Fill (padrão) ou um cálculo de tamanho personalizado.

Por exemplo, para definir uma página com 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 tamanho de página personalizado cálculo. Crie um PageSize e dividir a 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 do conteúdo. o que permite influenciar o tamanho e o alinhamento máximos das páginas.

Por exemplo, definir o padding de start alinha as páginas no final:

Pager com padding inicial mostrando o conteúdo alinhado ao 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:

Pager com padding inicial e final mostrando o conteúdo centralizado

val pagerState = rememberPagerState(pageCount = {
    4
})
HorizontalPager(
    state = pagerState,
    contentPadding = PaddingValues(horizontal = 32.dp),
) { page ->
    // page content
}

Definir o padding de end alinha as páginas mais para o início:

Pager com padding inicial e final mostrando o conteúdo alinhado ao 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 obter efeitos semelhantes para VerticalPager O valor 32.dp é usado apenas como exemplo. é possível definir cada uma das dimensões de padding para qualquer valor.

Personalizar o comportamento de rolagem

Os elementos combináveis padrão HorizontalPager e VerticalPager especificam como os gestos de rolagem funcionam com o pager. No entanto, é possível personalizar e alterar os padrões, como pagerSnapDistance ou flingBehavior.

Ajustar distância

Por padrão, HorizontalPager e VerticalPager definem o número máximo de páginas que um gesto de deslizar pode rolar até uma página de cada vez. Para alterar isso, definir 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),
        beyondBoundsPageCount = 10,
        flingBehavior = fling
    ) {
        PagerSampleItem(page = it)
    }
}