介绍一点Accompanist里面的库--Placeholder、SwipeRefresh

1,827 阅读9分钟

前言

这一篇文章里面我们继续认识几个Accompanist里面的组件,大家可以去这里查看Accompanist的所有组件库。

Placeholder

对于我们Android开发来讲,placeholder这个词一定不会陌生,我们在使用图片加载库Glide的时候就使用过它,当我们在加载一张网络图片的时候,为了不让图片在下载过程中ImageView展示区域显示空白,在上面覆盖一张占位图提示用户此处是一张正在加载中的图片

image.png

但这种做法只限于图片展示,如果想要展示非图片的其他数据,想要在请求数据时候也展示一张类似于占位图一样的视图,也就是骨架屏,我们不得不另外写一个View,通过数据是否加载成功来判断这个骨架屏的隐藏或展示,相当麻烦,代码也十分冗余,或者我们会封装一个Viewholder,将这段逻辑封装起来,但如果产品要求骨架屏的样式与实际展示的页面样式一样的时候,难道我们再去多创建几个Viewholder吗,那维护起来太累了,而在Accompanist里面就有一个组件帮我解决了这个问题,那就是Placeholder,通过在Modifier里面添加placeholder操作符,使得让任何一个组件都拥有了设置占位视图的能力,我们现在就来看下如何使用Placeholder

依赖地址

implementation “com.google.accompanist:accompanist-placeholder-material:<version>”
implementation "com.google.accompanist:accompanist-placeholder:<version>"

看着是有两个依赖地址,其实在开发时候任选其中一个就好了,这两个的区别在于placeholder-material在另一个的基础上,默认样式使用了MaterialTheme里面定义的样式,另一个placeholder,我们称为foundation版本,则完全由上层自定义样式,我们下面就以placeholder-material为例来模拟一个通过网络加载来展示数据的过程

如何使用

首先创建一个开关,默认情况下是关闭的,表示当前没有数据,然后使用Flow写一个五秒钟的定时器,五秒以后将开关打开,展示数据,定时器的代码如下

image.png

dataVisible就是我们的开关,默认值为false,在LaunchedEffect里面创建了一个定时器,count在五秒后将dataVisible设置成true,表示数据来了,接下来就是视图部分

image.png

当开关关闭的时候,我们只展示一个显示为"Loading...."的Text组件,当开关打开的时候,展示五个由图片与文字组成的小视图,这个过程像极了我们平时去实现一个展示或隐藏骨架屏的功能,来看下效果

0506aa2.gif

效果略微单调,从代码角度来讲if-else的代码也有点臃肿,现在我们的占位视图只是一个Text,如果占位视图复杂一点的话,else分支里面又是一大段布局代码,阅读起来还是比较吃力的,我们来看看用placeholder能有什么效果

0506aa3.gif

我们看到只是在TextImage组件里面的Modifier后面增加一个placeholder的操作符,然后将dataVisible作为参数传进去,表示没数据时候在组件上覆盖一层视图,有数据的时候再隐藏,代码变得精简许多,从运行结果也能看到效果也好了不少,并且placeholder还可以添加淡入淡出以及闪烁效果,先看下淡入淡出如何实现

0506aa4.gif

通过highlight参数设置了PlaceholderHighlight.fade()就完成了placeholder淡入淡出的效果,另外还可以使用PlaceholderHighlight.shimmer()来设置placeholder闪烁的效果,我们来看下

0506aa5.gif

另外可以在shimmer中传入颜色来改变闪烁的效果,比如像这样

0506aa6.gif

SwipeRefresh

Compose里面如果想要实现下拉刷新效果,除了可以利用一些手势操作api去制作以外,Accompanist还提供了SwipeRefresh组件来实现,类似于我们用过的SwipeRefreshLayout

依赖地址

implementation “com.google.accompanist:accompanist-swiperefresh:<version>

目前最新版本为0.31.1-alpha,但是在Compose 1.3.0里面已经将SwipeRefresh移入到了正式库,但是由于正式库里面对SwipeRefresh做了一定修改,有点差异,所以接下去会对Accompanist和正式库里面的SwipeRefresh都做下介绍

Accompanist里面的SwipeRefresh

如果想要实现下拉刷新,就要使用SwipeRefresh组件,然后在组件内部嵌入一个垂直可滑动的组件,比如LazyColumn,或者使用Modifier.verticalScroll操作符,我们继续使用上面的例子,在Column最外层套一个SwipeRefresh组件,并且在Column内部的Modifier里使用verticalScroll操作符

image.png

而通过查看SwipeRefresh源码,我们知道有两个参数为必传选项,分别是stateonRefresh

image.png

state传入一个SwipeRefreshState对象,通过rememberSwipeRefreshState创建,其中这个函数需要传入一个isRefreshing的状态位,这个有现成的,就是之前展示和隐藏placeholder的开关dataVisible,除了state还需要传入一个onRefresh,这是一个函数类型,看名字就知道这里就是执行刷新操作的地方,比如网络请求,而在我们这个例子里面,直接去更新LaunchedEffect函数的key就好

image.png

新创建了一个Int类型的key作为LaunchedEffect函数的入参,执行刷新操作的时候,对key进行自增操作,就触发LaunchedEffect里面的协程再一次运行,效果如下

0506aa7.gif

一个基本的下拉刷新功能就完成了,而当我们在做一个下拉刷新功能的时候,除了要实现它基本的刷新后更新数据之外,还要考虑如何去更改样式,自定义样式,现在基本主流的下拉刷新组件都提供了自定义样式的功能,我们这个SwipeRefresh能自定义吗?当然也能,就是用它的indicator参数

image.png

我们看到源码中indicator默认实现了一个叫SwipeRefreshIndicator的组件,不用猜肯定知道这个肯定是我们效果图里面那个转圈圈带箭头的那个组件,我们看下SwipeRefreshIndicator都提供了哪些参数

image.png

看到有触发刷新的滑动距离的参数refreshTriggerDistance,默认是80dp,还有是否需要带箭头的参数arrowEnabled,背景色以及内容颜色,还有刷新组件的形状等,我们如果想要更改一下刷新组件的样式,可以在上层重写一个SwipeRefreshIndicator,然后重新对上面这些参数赋值就好,比如现在对刷新组件做了如下更改

0507aa1.gif

这里我们对刷新组件的背景色以及箭头颜色做了修改,还去掉了箭头效果,然后将形状也变成了圆角矩形,我们还将largeIndication参数设置为true,这个参数有啥用呢?false的时候组件用的是默认大小40x40dp的,变成true的时候尺寸就会大些,变成56x56dp,最终达到的效果就如同效果图那样,那我们除了这样的效果之外,就不能用其他效果了吗?也可以,咱不用SwipeRefreshIndicator这个组件不就好了吗,只需要靠state这个参数就可以另外写一个效果,state是啥,它是个SwipeRefreshState对象,它里面封装了刷新时候的状态

image.png

公开出来的有这三个参数,isRefreshing表示是否正在刷新,isSwipeInProgress表示是否正在拖拽,indicatorOffset表示拖拽的距离,那可以利用这三个参数,实现一个比较原始的文字刷新效果

image.png

首先我们创建两个变量,indicatorH表示拖拽的距离,refreshState是刷新状态,并在indicator中通过SwipeRefreshState对这俩变量赋值,当refreshState是1的时候表示拖拽中,2的时候表示刷新中,0表示正常状态

image.png

随后我们在原来的数据列表上面加一个视图用来当作刷新视图,刷新视图里面只有一个Text组件,Text的文案会根据refreshState改变而改变,此外刷新视图是个Row,它的高度会首先跟随着indicatorH的值并且不超过80dp,我们看下这个自定义的效果如何

0507aa2.gif

回到正常状态看起来有点不丝滑,原因是直接把高度变成0了,我们可以在这个过程加点动效,这样可以让效果变得好看些,不过这种优化不属于这次讨论的重点就不说了,我们知道如何自定义刷新效果就好,接下来看下SwipeRefresh在正式库里面是如何使用的

正式库里面的SwipeRefresh

SwipeRefresh被挪入到正式库以后,有以下需要注意的变化

  1. 废弃SwipeRefresh组件,更改为Modifier.pullRefresh操作符
  2. rememberSwipeRefreshState更改为rememberPullRefreshState
  3. SwipeRefreshIndicator变成了PullRefreshIndicator

总结起来就是使用起来更加灵活,任何支持垂直方向滚动的组件都可以实现下拉刷新效果,不用在特意为了实现下拉刷新而在组件最外层加上SwipeRefresh组件,现在我们使用正式库里面的下拉刷新功能更改一下上面的例子,首先看下Modifier.pullRefresh需要哪些参数

image.png

看起来只需要一个PullRefreshState,那么我们就先来把它创建出来

image.png

这里看到必填的有两个参数,一个是refreshing,另一个是onRefresh,都是刚才已经实现过的,把代码直接带进去就好了

image.png

接下去在数据列表的外层创建个父布局Box,父布局里面使用Modifier.pullRefresh

image.png

然后在Column的同级位置加上我们刷新组件PullRefreshIndicator

0508aa1.gif

PullRefreshIndicator的必传参数也只有两个,一个是刷新的状态位dataVisible,另一个是刚刚生成的PullRefreshState,最终达到的默认效果跟之前使用SwipeRefresh达到的默认效果是一致的

总结

这篇文章介绍了PlaceholderSwipeRefresh两个库,个人感觉这两个库所被应用的场景应该是最广泛的,涉及到加载数据或者下拉刷新的场景都可以使用,并且可以搭配在一起使用,希望对各位有所帮助。