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

news2024/10/6 10:50:30

Compose pager分页器入门使用

  • 前言
  • 依赖
  • 概念介绍
    • 参数介绍
  • 使用
    • 基础使用
    • 规范使用
    • 跳转指定分页器 pagerState.scrollToPage()
    • 添加指示器 pagerState.currentPage
  • 完整代码
  • 总结

前言

阅读本文需要一定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,我们还可以实现点击指示器就跳转指定分页器的常用功能,有兴趣的同学可以试下,本文主要参考官方文档学习总结,更多的用法官方文档也有介绍,这里就不赘述了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/939661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

说说我最近招人的感受。。

大家好&#xff0c;我是鱼皮。 都说现在行情不好、找工作难&#xff0c;但招人又谈何容易&#xff1f;&#xff01; 最近我们公司在招开发&#xff0c;实习社招都有。我收到的简历很多&#xff0c;但认真投递的、符合要求的却寥寥无几&#xff0c;而且都是我自己看简历、选人…

【EI检索稳定】第六届电力电子与控制工程国际学术会议(ICPECE 2023)

第六届电力电子与控制工程国际学术会议 2023 6th International Conference on Power Electronics and Control Engineering (ICPECE 2023) 第六届电力电子与控制工程国际学术会议由广西大学主办&#xff0c;重庆大学、华东交通大学、长春理工大学、大连交通大学联合主办。电…

伦敦金走势多变怎么办

投资知识比较丰富的朋友&#xff0c;应该知道一个品种的价格过于波动&#xff0c;对投资者来说并是一件不友好的事情&#xff0c;因为频繁的价格变化&#xff0c;对于收益的稳定性会产生负面的影响&#xff0c;也可能让投资者的持仓陷入进退维谷的尴尬境地。 黄金作为贵金属市场…

【视觉系统】笔芯内径机器视觉测量软硬件方案-康耐德智能

检测内容 笔芯内径机器视觉测量系统 检测要求 精度0.03mm&#xff0c;速度120~180个/分钟 视觉可行性分析 对样品进行了光学实验&#xff0c;并进行图像处理&#xff0c;原则上可以使用机器视觉系统进行测试测量。 结果&#xff1a; 对所有样品进行分析&#xff0c;可以在不…

抽象工厂(Abstract Factory)模式

一、 抽象工厂&#xff08;Abstract Factory&#xff09;模式 抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。 为了方便引进抽象工厂模式&#xff0c;引进一个新概念&#xff1a;产品族&#xff08;Product Family&#xff09;。所谓产品族&#xff0c…

16- flask-bootstrap模板的使用

Flask 中支持 flask-bootstrap模板 和 bootstrap-flask模板 # 不使用: bootstrap-flask # pip install bootstrap-flask1.3.1 # 支持bootstrap 4 # pip install flask-bootstrap # 支持bootstrap3# 中文文档:https://flask-bootstrap-zh.readthedocs.io/zh/latest/ # 样式文档…

yolov8 No labels found in /path/train.cache解决

在用yolov8中ultralytics/datasets/coco.yaml训练时出现了no labels found in train2017.cache的错误。 model.train(data"ultralytics/datasets/coco.yaml",epochs100,imgsz640,batch16,save_period10,)下面查找一个这个问题出现的原因。 这里并没有提前下载coco数…

VM安装Windows11

VM下载地址&#xff1a; https://www.vmware.com/cn/products/workstation-pro/workstation-pro-evaluation.html Windows11镜像下载地址&#xff1a; https://www.microsoft.com/zh-cn/software-download/windows11/ 选择刚下载的Windows11镜像 更改安装目录 设置密码 …

MySQL索引,事务和存储引擎

一、索引 1、索引的概念 ●索引是一个排序的列表&#xff0c;在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址&#xff08;类似于C语言的链表通过指针指向数据记录的内存地址&#xff09;。 ●使用索引后可以不用扫描全表来定位某行的数据&#xff0c;而是先…

ASIC-WORLD Verilog(13)状态机FSM

写在前面 在自己准备写一些简单的verilog教程之前&#xff0c;参考了许多资料----Asic-World网站的这套verilog教程即是其一。这套教程写得极好&#xff0c;奈何没有中文&#xff0c;在下只好斗胆翻译过来&#xff08;加点自己的理解&#xff09;分享给大家。 这是网站原文&…

MySQL 8.1.0 推出 InnoDB Cluster 只读副本

全面了解 8.1.0 版本新功能&#xff1a;InnoDB Cluster 只读副本的相关操作。 作者&#xff1a;Miguel Arajo 高级软件工程师 / Kenny Gryp MySQL 产品总监 本文来源&#xff1a;Oracle MySQL 官网博客 * 爱可生开源社区出品。 前言 MySQL 的第一个 Innovation 版本 8.1.0 已…

Pyqt5开发实战记录

入职以来第一个开发项目&#xff1a; 1、如何给Qlabel加边框&#xff1a;右键label对象&#xff0c;选择“改变样式表”输入一下代码&#xff1a; border: 1px solid black;2、如何让垂直布局中button大小不发生变化&#xff1a;其实很简单&#xff0c;只需要设置button的最大…

(视频教程)单细胞转录组多组差异基因分析及可视化函数

很久以前&#xff0c;我们发布过一个单细胞多组差异基因可视化的方法。跟着Cell学单细胞转录组分析(八):单细胞转录组差异基因分析及多组结果可视化。主要复现参考的是这篇发表在《Cell》上的文章。可以将多个组的差异结果展示出来。 &#xff08;reference&#xff1a;A Spati…

夜莺项目发布 v6.1.0 版本,增强可观测性数据串联

大家好&#xff0c;夜莺项目发布 v6.1.0 版本&#xff0c;这是一个中版本迭代&#xff0c;不止是 bugfix 了&#xff0c;而是引入了既有功能的增强。具体增强了什么功能&#xff0c;下面一一介绍。 1. 增强可观测性数据串联 从 v6.1.0 开始&#xff0c;对日志索引模式做了增强…

TS-小技巧-持续更新

文章目录 一、类型小技巧1. Partial 的应用2. Pick 的应用3. Parameters 的应用4. ReturnType 的应用 一、类型小技巧 1. Partial 的应用 interface User {name: string;age: number;address: string}获取接口User的所有属性&#xff0c;且不确定属性是否全部需要: type UserP…

ceph peering机制-状态机

本章介绍ceph中比较复杂的模块&#xff1a; Peering机制。该过程保障PG内各个副本之间数据的一致性&#xff0c;并实现PG的各种状态的维护和转换。本章首先介绍boost库的statechart状态机基本知识&#xff0c;Ceph使用它来管理PG的状态转换。其次介绍PG的创建过程以及相应的状…

Element-Ul中的Mess消息不能正常显示

项目场景 注册 使用Element-Ul中的表单&#xff0c;Mess消息不能正常显示 预计效果 Part1 问题1 给按钮添加Mess消息提示,有反应&#xff0c;但不是预期反应&#xff0c;就没看到页面上方的提示框&#xff0c;就是滚动条一直上下动 后来发现是提示框都在下面出现了 <scr…

IdentityServer密码长度超长会导致跳转到登录页

应用系统项目的安全要求越来越高&#xff0c;基本都是采取https等加密证书传输&#xff0c;无法使用https的&#xff0c;也是要求不能明文传输内容&#xff0c;因此做一些等保要求&#xff0c;密码需要加密后才能传输给服务端&#xff0c;所以前端会采取一些密码手段&#xff0…

C# Linq源码分析之Take(四)

概要 本文主要对Take的优化方法进行源码分析&#xff0c;分析Take在配合Select&#xff0c;Where等常用的Linq扩展方法使用时候&#xff0c;如何实现优化处理。 本文涉及到Select, Where和Take和三个方法的源码分析&#xff0c;其中Select, Where, Take更详尽的源码分析&…

数据结构--树4.2.2(二叉树--遍历)

目录 一、二叉树的建立 二、二叉树的遍历算法 一、二叉树的建立 CreateBitree(Bitree *t){char c;scanf("%c",&c);if( c){*t NULL;}else{*t(Bitnode*)malloc(sizeof(Bitnode));(*t)->data c;CreateBitree(&(*t)->lchild);CreateBitree(&(*t)-&…