快来学习 Compose 中的 “ViewPager” 吧

什么是 ViewPager

对啊,什么是 ViewPager 呢?ViewPager 的功能就是可以使视图滑动,就像桌面左右滑动那样。写过安卓的对它可谓是非常熟悉了,不管是轮播图的使用、亦或是页面之间的切换,都离不开它,先来看看在安卓中的 ViewPager 实现效果吧:

ViewPager1.gif

上图中就是 ViewPager 作为轮播图的简单例子。

顺嘴提一下吧,之前安卓的 ViewPager 确实只能实现左右的滑动,但是在 2019 年的时候谷歌就推出了 ViewPager2ViewPager2 不但可以支持左右滑动,也可以实现竖向的滑动,这样可玩性又提高了许多,也不需要为了产品的一些特定需求而去自定义 ViewGroup 了。

Compose 中的 “ViewPager”

其实 Compose 中并没有 ViewPager ,而是谷歌在 Compose 的扩展库中写了一个名叫 Pager 的库,实现了类似安卓中 ViewPager 的功能。

添加依赖

上面也提到了, Compose 中并没有 ViewPager ,所以需要在 build.gradle 中添加下依赖:

[[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h0bQBx1T-1645407892234)()]

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.google.accompanist:accompanist-pager:<version>"
}

将上面的 version 替换成最新的版本号即可,笔者在写的时候最新版本为 0.24.2-alpha

水平滑动(左右)

添加好依赖来看看如何使用吧!

HorizontalPager(count = 10) { page ->
    Text(text = "Page: $page")
}

没错,就这么简单,还记得之前在 Android View 中使用 ViewPager 有多费劲嘛。。。。在 Compose 中只需要一行代码。

下面来看下预览图吧,如果在 Android View 中想看 ViewPager 预览的话必须得运行到真机或者虚拟机来可以看到,但是在 Compose 中直接就可以进行预览。下面来写下预览代码吧:

@OptIn(ExperimentalPagerApi::class)
@Preview(name = "Test1", heightDp = 170)
@Composable
fun PagerTest1Preview() {
    PagerTest1()
}

简单介绍上上面的预览代码吧,先看第一行注解 OptIn,这是由于 Pager 库目前处于试验阶段,API 随时都可能修改,所以所有 API 都标有@ExperimentalPagerApi注释,第二行注解 Preview,设定了预览图的名字和高度。来看下效果吧:

ViewPager2.gif

这里由于是直接在 as 中进行的预览互动,所以看着可能有些卡顿,但在真机上没有问题,如德芙般丝滑。

垂直滑动(上下)

上面写了水平滑动,这里来看下垂直滑动吧:

VerticalPager(count = 10) { page ->
    Text(text = "Page: $page")
}

很容易理解吧,水平滑动是 HorizontalPager,垂直滑动就是 VerticalPager,具体详细使用在下面篇幅中介绍,这里先来看看垂直滑动的预览效果吧:

@OptIn(ExperimentalPagerApi::class)
@Preview(name = "Test3", heightDp = 170, widthDp = 100)
@Composable
fun PagerTest3Preview() {
    PagerTest3()
}

预览代码和刚才差不多,就不做过多解释了,直接来看效果图吧:

ViewPager3.gif

不错吧,很简单的代码就实现了之前在 Android View 中比较复杂的效果。

源码解析

下面咱们来简单看看 Pager 的源码吧。

水平滑动源码

先来看看水平滑动的源码:

image.png

由于怕大家看着不舒服,所以这里的代码放成了图片,下面来看下可组合项 HorizontalPager 的参数都有什么作用吧,或者说可组合项 HorizontalPager 都有什么功能。

  • count:这是可组合项 HorizontalPager 必须要写的参数,顾名思义,就是 Page 的数量
  • Modifier:这个在 Compose 中见的太多了,不再赘述,修饰符
  • state:用于控制或观察 Pager 状态的状态对象
  • reverseLayout:反转滚动和布局的方向,默认为 false,为 true 时第一项将在最后
  • itemSpacing:添加项目之间的垂直间距
  • contentPadding:内容的 Padding 值
  • verticalAlignment:垂直对齐为居中
  • flingBehavior:描述滑动行为的逻辑
  • key:滚动的位置将根据键保持
  • userScrollEnabled:是否允许通过用户手势或辅助功能进行滚动。即使它被禁用,仍然可以使用该状态以编程方式滚动
  • content:具体可组合项的内容,在这里,可以引用 PagerScope.currentPage 和 PagerScope 中的其他属性。

垂直滑动源码

接下来再来看下垂直滑动的源码:

image.png

发现点什么没有,水平滑动和垂直滑动的源码是不是非常像,而且都是直接调用的 Pager 可组合项。这里有很多参数和水平滑动的一样,就不再赘述,来看看不同的参数吧。

  • horizontalAlignment:上面水平滑动的是 verticalAlignment 垂直对齐居中,这里是水平对齐居中
  • flingBehavior:描述滑动行为的逻辑也有所不同

剩下就基本一致了。

Pager 内部实现

下面来看下 Pager 内部的实现吧,同样因为这里是源码,所以还放成图片吧:

image.png

可以看出 Pager 可组合项中有一个 isVertical 参数,用来判断是垂直滑动还是水平滑动,如果是垂直滑动的话用的是 LazyColumn,水平滑动则用的是 LazyRow,还记得吗?在 Compose 中 LazyColumn 和 LazyRow 对标的是 Android View 中的 RecyclerView,值得一说的是,现在 Android View 中的 ViewPager2 也使用的是 RecyclerView。

进阶用法

上面大概说了下 Pager 的基本用法,但如果在生产环境中一般不会这么简单的,这里来举几个生产环境中可能是用到的例子吧。

添加指示器

什么是 指示器呢?很简单,就是文章开头那张动图中间下方的小圆圈,用来告知用户当前在第几页以及一共有几页。

之前想在 Compose 中添加指示器需要自己来绘制,但现在官方也创建了一个库专门 用来实现 Pager 的指示器,来看看吧。

添加依赖

第一步还是在 build.gradle 中添加依赖:

[[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cS6d9TU0-1645407892238)()]

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.google.accompanist:accompanist-pager:<version>"

    // 指示器
    implementation "com.google.accompanist:accompanist-pager-indicators:<version>"
}

同上面一样,使用的时候需要将 version 替换成当前最新的版本,笔者在写的时候最新版本为 0.24.2-alpha

使用指示器

来看下如何使用指示器吧:

// 水平指示器
HorizontalPagerIndicator(
    pagerState = pagerState,
    modifier = Modifier
        .align(Alignment.CenterHorizontally)
        .padding(16.dp),
)

// 垂直指示器
VerticalPagerIndicator(
    pagerState = pagerState,
    modifier = Modifier
        .align(Alignment.CenterHorizontally)
        .padding(16.dp),
)

是不是 so easy,指示器也是一个可组合项,只需要你传入 Pager 的状态即可,总页数以及当前的页码在 PagerState 中都有。下面先来看下效果图吧:

viewPager5.gif

通过上面代码可以发现指示器也分为水平和垂直的,由于水平的指示器使用较多,所以本文以水平指示器作为讲解对象。

image.png

上图就是水平指示器的源码,其实和我之前绘制的原理都一样,不过当官方推出库之后肯定选择用官方的😂。方法中的绘制代码不过多赘述,大家应该都能看懂,下面来说一下参数的含义吧:

  • pagerState:Pager 的状态
  • modifier:修饰符,不过多解释
  • activeColor:当前活跃的指示器颜色
  • inactiveColor:当前不活跃的指示器颜色
  • indicatorWidth:指示器的宽度
  • indicatorHeight:指示器的高度
  • spacing:指示器之间的宽度
  • indicatorShape:指示器的形状,默认为圆形,大家也可以自己进行定义

指示器还可以与 TabRow 进行结合使用,效果和微信的页面切换效果基本一致,大家感兴趣的可以自己下去试一下。

监听页面更改

在 Android View 中咱们如果想监听 ViewPager 的话只需要通过 registerOnPageChangeCallback 方法即可进行监听,那么在 Compose 中咱们应该如何监听 Pager 的页面更改呢?

每当所选页面发生更改时,都会更新该 PagerState.currentPage 属性。您可以使用该 snapshotFlow 函数来观察流的变化:

val pagerState = rememberPagerState()
LaunchedEffect(pagerState) {
    snapshotFlow { pagerState.currentPage }.collect { page ->
        AnalyticsService.sendPageSelectedEvent(page)
    }
}
VerticalPager(
    count = 10,
    state = pagerState,
) { page ->
    Text(text = "Page: $page")
}

修改 Pager 切换效果

大家都知道,在 Android View 中,ViewPager 的切换效果可以通过设置 PageTransformer 来进行修改。来看下官方提供 Demo 的样例吧:

viewPager4.gif

看着效果还不错吧,由于里面涉及知识点太多,想学习的可以去官方的源码中进行查看,https://github.com/google/accompanist/blob/main/sample/src/main/java/com/google/accompanist/sample/pager/HorizontalPagerTransitionSample.kt

暂时小结

如果大家想系统地学习 Compose 的话,可以购买我的新书《Jetpack Compose:Android全新UI编程》进行阅读,里面有完整的 Compose 框架供大家学习。

京东购买地址

当当购买地址

今天的代码样例都在 玩天气 Github:https://github.com/zhujiang521/PlayWeather

如果对你有帮助的话,别忘记点个 Star,感激不尽。

其实还有一些细节的东西我没有说到,大家如果有疑问的话可以在评论区提出来。

好了,先写到这里吧,再会!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容