
Yasha
原本是一个Android RecyclerView的一个框架,通过提供简洁易用的DSL语法而避免使用繁杂原始的Adapter API,并集成Sange提供的具备分页功能的DataSource,实现快速渲染分页列表的功能。
在Compose大行其道的今天,Yasha
也紧随其后,推出了全新的Yasha-Compose
,现在不仅可以使用Yasha渲染原生的RecyclerView和ViewPager分页列表,还可以渲染Compose的LazyList并优雅高效处理分页逻辑。
下面就来看看如果使用Yasha-Compose
吧。
首先需要添加Yasha-Compose
依赖:
groovy
repositories {
//add jitpack
maven { url 'https://jitpack.io' }
}
dependencies {
// add yasha-compose
implementation "com.github.ssseasonnn.Yasha:yasha-compose:1.2.0"
}
在开始渲染列表之前,首先需要创建DataSource,通过继承ComposeDataSource
并实现loadInitial()
和loadAfter()
方法,即可处理数据的初始化加载和分页加载逻辑。如下面所示:
kotlin
class DemoDataSource(coroutineScope: CoroutineScope) : ComposeDataSource(coroutineScope) {
override suspend fun loadInitial(): List<ComposeItem>? {
// handle load initial
// ...
}
override suspend fun loadAfter(): List<ComposeItem>? {
// handle load after
// ...
}
}
loadInitial()
将会在DataSource与LazyList绑定时自动调用,因此这里是我们加载初始数据的最佳地点,我们可以在这个方法中去访问HTTP API接口或者是从数据库读取数据,需要注意的是,loadInitial
在主线程调用,因此当访问API接口或操作数据库时,需要通过withContext(Dispathers.IO)
切换至IO线程,例如:
kotlin
override suspend fun loadInitial(): List<ComposeItem>? {
val result = withContext(Dispatchers.IO) {
ApiClient.getUsers()
}
return result
}
在loadInitial()
中, 通过返回的数据来决定当前的加载状态。如果返回emptyList()
,则代表页面为空,将会停止加载及后续的分页加载。如果返回null
,则代表初始化加载失败,将会触发加载失败状态。如果返回非空的List,则代表初始化列表加载成功,同时页面会渲染加载成功的列表。例如:
kotlin
override suspend fun loadInitial(): List<ComposeItem>? {
// 加载成功
val result = listOf(item1,item2)
return result
// 加载失败
return null
// 加载结束,空页面
return emptyList
}
loadAfter()
将会在列表滑动到末尾时自动调用,因此可以在这里处理加载分页数据的逻辑,同样的loadAfter
也在主线程调用,因此当访问API接口或操作数据库时,需要通过withContext(Dispathers.IO)
切换至IO线程,例如:
kotlin
override suspend fun loadAfter(): List<ComposeItem>? {
val result = withContext(Dispatchers.IO) {
ApiClient.getUsers()
}
return result
}
在loadAfter()
中, 同样会通过返回的数据来决定当前的分页加载状态。如果返回emptyList()
,则代表分页加载结束。如果返回null
,则代表分页加载失败,将会触发加载失败状态。如果返回非空的List,则代表分页加载成功,此时页面可以渲染加载成功的分页列表。例如:
kotlin
override suspend fun loadAfter(): List<ComposeItem>? {
// 加载成功
val result = listOf(item1,item2)
return result
// 加载失败
return null
// 加载结束
return emptyList
}
完整的DataSource示例实现如下:
kotlin
class DemoDataSource(coroutineScope: CoroutineScope) : ComposeDataSource(coroutineScope,enableDefaultState = false) {
var page = 0
override suspend fun loadInitial(): List<ComposeItem>? {
page = 0
val items = mutableListOf<ComposeItem>()
for (i in 0 until 3) {
items.add(NormalItem(i))
}
return items
}
override suspend fun loadAfter(): List<ComposeItem>? {
page++
delay(2000)
//Mock load failed.
if (page % 5 == 0) {
return null
}
val items = mutableListOf<ComposeItem>()
for (i in page * 3 until (page + 1) * 3) {
items.add(NormalItem(i))
}
return items
}
}
在创建好DataSource之后,就可以渲染LazyList了,例如渲染一个LazyColumn:
kotlin
val dataList = demoDataSource.dataFlow.collectAsState(emptyList())
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(dataList.value) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(10.dp)
.background(Color.Gray, RoundedCornerShape(8.dp)),
contentAlignment = Alignment.CenterStart
) {
Text(text = "Item $it")
}
}
}
现在运行一下看下效果了:

分页加载似乎没问题了,可是缺少点东西,是的,缺少了分页加载中的loading提示以及分页加载失败后的retry按钮。
不用担心,Yasha-Compose
已经为你考虑到这点了。下面让我们快速实现一个分页加载提示。
我们注意到之前的ComposeDataSource构造函数中有一个额外参数enableDefaultState = false
,现在让我们将它改为true
,这样我们的DataSource中将会有一个默认的StateItem,类型为ComposeStateItem。同时Yasha-Compose为它提供了一个默认的ComposeStateItemUI。
一切准备就绪,现在让我们来快速渲染一下这个StateItem吧:
kotlin
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(dataList.value) {
// 渲染分页加载状态
if (it is ComposeStateItem) {
ComposeStateItemUI(it)
} else {
Box(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(10.dp)
.background(Color.Gray, RoundedCornerShape(8.dp)),
contentAlignment = Alignment.CenterStart
) {
Text(text = "Item $it")
}
}
}
}
运行效果图如下:

等等,下拉刷新呢?
放心,简单得不能再简单了,DataSource提供了invalidate()
方法,可以让你随时随地自由的刷新,当invalidate
方法调用后,将会清空列表并重新触发loadInitial
方法,就像第一次加载一样。
kotlin
// DataSource
class DemoDataSource(coroutineScope: CoroutineScope) : ComposeDataSource(coroutineScope, enableDefaultState = true) {
val isRefreshing = MutableStateFlow(false)
var page = 0
override suspend fun loadInitial(): List<ComposeItem>? {
page = 0
isRefreshing.value = true
val items = mutableListOf<ComposeItem>()
for (i in 0 until 3) {
items.add(NormalItem(i))
}
//mock loading
delay(1000)
isRefreshing.value = false
return items
}
override suspend fun loadAfter(): List<ComposeItem>? {
page++
delay(2000)
//Mock load failed.
if (page % 5 == 0) {
return null
}
val items = mutableListOf<ComposeItem>()
for (i in page * 3 until (page + 1) * 3) {
items.add(NormalItem(i))
}
return items
}
}
// UI
val dataList = demoDataSource.dataFlow.collectAsState(emptyList())
val isRefreshing = demoDataSource.isRefreshing.collectAsState()
SwipeRefresh(
state = rememberSwipeRefreshState(isRefreshing.value),
onRefresh = {
demoDataSource.invalidate(true)
},
) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(dataList.value) {
if (it is ComposeStateItem) {
ComposeStateItemUI(it)
} else {
Box(
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(10.dp)
.background(Color.Gray, RoundedCornerShape(8.dp)),
contentAlignment = Alignment.CenterStart
) {
Text(text = "Item $it")
}
}
}
}
}
运行效果:

好了,大功告成,现在你已经学会了Yasha-Compose的基本用法了。有关Yasha的更多用法,可以去Github查看详细的demo:Yasha