Compose pager分页器入门使用 HorizontalPager与VerticalPager(2023/8)

前言

阅读本文需要一定compose基础,如果没有请移步Jetpack Compose入门详解(实时更新)

本文介绍Compose pager分页器, pager分页器 就是viewpager2的compose版本;这还是一个实验性api,在此之前依赖已经有一次迁移

依赖

    implementation 'androidx.compose.foundation:foundation:1.4.3'
    //已弃用
    //implementation "com.google.accompanist:accompanist-pager:0.33.1-alpha"

上面的版本号是写本文时最稳定的版本


概念介绍

pager分为横向的HorizontalPager与纵向的VerticalPager,它们继承的基类为Pager

Pager
HorizontalPager
VerticalPager

参数介绍

如果你使用过viewpager或viewpager2,相信你对这些属性一定不陌生

在这里插入图片描述

  • pageCount-此分页器的页数

  • modifier-修饰符

  • state-控制此分页器的状态

  • contentPadding-分页器的padding值。这将在内容被剪切后为其添加填充,这是通过修饰符param无法实现的。您可以使用它在第一页之前或最后一页之后添加填充。也可以使用pageSpacing添加页面之间的间距。

  • pageSize-使用此选项可以更改页面在此分页器中的填充方式。分别为填充满Fill,自适应Fixed和通过calculateMainAxisPageSize自己设置,默认Fill

  • beyondBoundsPageCount-在可见页面列表之前和之后加载的页面。注意:请注意,使用较大的beyondBoundsPageCount值会导致大量页面被合成、测量和放置,这将破坏使用延迟加载的目的。这应该被用作优化,以便在可见页面之前和之后预加载几个页面。(简单来说就是预加载页面)

  • pageSpacing-用于分隔此分页器中页面的空间量

  • flingBehavior-用于滚动后手势的flingBehavior。

  • userScrollEnabled-是否允许通过用户手势或辅助功能操作进行滚动。即使禁用PagerState.scroll,您仍然可以使用它以编程方式滚动。

  • reverseLayout-反转滚动和布局的方向。

  • key-表示项目的稳定且唯一的密钥。当您指定键时,滚动位置将根据键保持,这意味着如果您在当前可见项目之前添加/删除项目,则具有给定键的项目将保留为第一个可见项目。
    pageNestedScrollConnection-一个嵌套的ScrollConnection,用于指示此Pager如何使用嵌套列表。默认行为将使Pager消耗所有嵌套的delta。

HorizontalPager

  • verticalAlignment-页面在此Pager中垂直对齐方式。

VerticalPager

  • horizontalAlignment -页面在此Pager中的水平对齐方式。

使用

基础使用

示例代码:

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Greeting() {
    
    

        HorizontalPager(pageCount = 20) {
    
     page ->

               Text(
                   text = "Page: $page",
                   modifier = Modifier
                       .fillMaxWidth()
                       .height(170.dp)
                       .background(Color.Yellow)
               )


        }
}

效果:

在这里插入图片描述

上诉示例代码中,实现了一个非常简单的分页器,在实际开发中我们有以下几点需要注意:

  1. Pager可组合项必带pageCount参数,否则会报错,其他都是可选参数
  2. PagerState应该被初始化,最好不要用自带的以方便控制分页器
  3. 结合实际情况灵活使用beyondBoundsPageCount,在性能和用户体验上获得最佳平衡

规范使用

根据上面的几点,我们可以将上例代码更改为

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Greeting() {
    
    

        val list = mutableListOf<Int>()
        list.add(R.mipmap.advocate)
        list.add(R.mipmap.arabianman)
        list.add(R.mipmap.boy)

        val pagerState = rememberPagerState(
            1
        )


        HorizontalPager(pageCount = 3,
            state = pagerState,
            beyondBoundsPageCount = 1
            ) {
    
     page ->
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(170.dp)
                    .background(Color.Yellow)
            ) {
    
    
                Image(painter = painterResource(id = list[page]) ,
                    modifier = Modifier
                        .align(alignment = Alignment.CenterHorizontally)
                        .padding(top = 25.dp),
                    contentDescription = "person" )
                Text(
                    text = "Page: $page",
                    Modifier.align(alignment = Alignment.CenterHorizontally)
                )
            }
        }
}

效果如下:
在这里插入图片描述

我们将rememberPagerState设置为1,所以分页器的第一个展示是从1开始的;同理,我们将HorizontalPager换为VerticalPager,其他代码不变,效果如下:
在这里插入图片描述

跳转指定分页器 pagerState.scrollToPage()

如果我们想跳转指定的分页器,可以使用将示例代码更改为如下

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Greeting() {
    
    

        val list = mutableListOf<Int>()
        list.add(R.mipmap.advocate)
        list.add(R.mipmap.arabianman)
        list.add(R.mipmap.boy)

        val pagerState = rememberPagerState(
            0
        )

    val coroutineScope = rememberCoroutineScope()

    Column() {
    
    
        HorizontalPager(pageCount = 3,
            state = pagerState,
            beyondBoundsPageCount = 1
        ) {
    
     page ->
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(170.dp)
                    .background(Color.Yellow)
            ) {
    
    
                Image(painter = painterResource(id = list[page]) ,
                    modifier = Modifier
                        .align(alignment = Alignment.CenterHorizontally)
                        .padding(top = 25.dp),
                    contentDescription = "person" )
                Text(
                    text = "Page: $page",
                    Modifier.align(alignment = Alignment.CenterHorizontally)
                )
            }
        }


        Button(onClick = {
    
    
            coroutineScope.launch {
    
    
                // Call scroll to on pagerState
                pagerState.scrollToPage(3)
            }
        }) {
    
    
            Text("跳转到页面3")
        }
    }

}

效果如下:

在这里插入图片描述
上例代码中,我们使用了PagerState.scrollToPage方法来实现点击按钮跳转到指定分页器的效果,如果需要动画,可以使用 pagerState.animateScrollToPage() 方法

添加指示器 pagerState.currentPage

如果想添加分页器的指示器,只需要添加如下代码

val pageCount = 3

...

 Row(
            Modifier
                .height(50.dp)
                .fillMaxWidth(),
            horizontalArrangement = Arrangement.Center
        ) {
    
    
            repeat(pageCount) {
    
     iteration ->
                val color =
                    if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray
                Box(
                    modifier = Modifier
                        .padding(2.dp)
                        .clip(CircleShape)
                        .background(color)
                        .size(10.dp)

                )
            }
        }

效果如下:

在这里插入图片描述
上例代码中我们使用pagerState.currentPage 方法来判断是否选择了当前的选择器。

完整代码

package com.zyf.composepager

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement

import androidx.compose.foundation.layout.Box

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.VerticalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

import com.zyf.composepager.ui.theme.ComposepagerTheme
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContent {
    
    
            ComposepagerTheme {
    
    
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
    
    
                    Greeting()
                }
            }
        }
    }
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Greeting() {
    
    

    val list = mutableListOf<Int>()
    list.add(R.mipmap.advocate)
    list.add(R.mipmap.arabianman)
    list.add(R.mipmap.boy)

    val pagerState = rememberPagerState(
        0
    )

    val coroutineScope = rememberCoroutineScope()

    val pageCount = 3


    Column() {
    
    
        HorizontalPager(
            pageCount = 3,
            state = pagerState,
            beyondBoundsPageCount = 1,
            modifier = Modifier .background(Color.Yellow)
        ) {
    
     page ->
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(170.dp)
            ) {
    
    
                Image(
                    painter = painterResource(id = list[page]),
                    modifier = Modifier
                        .align(alignment = Alignment.CenterHorizontally)
                        .padding(top = 25.dp),
                    contentDescription = "person"
                )
                Text(
                    text = "Page: $page",
                    Modifier.align(alignment = Alignment.CenterHorizontally)
                )
            }
        }


        Row(
            Modifier
                .height(50.dp)
                .fillMaxWidth(),
            horizontalArrangement = Arrangement.Center
        ) {
    
    
            repeat(pageCount) {
    
     iteration ->
                val color =
                    if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray
                Box(
                    modifier = Modifier
                        .padding(2.dp)
                        .clip(CircleShape)
                        .background(color)
                        .size(10.dp)

                )
            }
        }


        Button(onClick = {
    
    
            coroutineScope.launch {
    
    
                pagerState.animateScrollToPage(3)
            }
        }) {
    
    
            Text("跳转到页面3")
        }

    }
}

@Preview(showBackground = true, showSystemUi = true)
@Composable
fun GreetingPreview() {
    
    
    ComposepagerTheme {
    
    
        Greeting()
    }
}

总结

通过使用pagerState.currentPage和pagerState.scrollToPage,我们还可以实现点击指示器就跳转指定分页器的常用功能,有兴趣的同学可以试下,本文主要参考官方文档学习总结,更多的用法官方文档也有介绍,这里就不赘述了。

猜你喜欢

转载自blog.csdn.net/shop_and_sleep/article/details/132535523