如何用Yasha-Compose渲染一个带分页功能的Compose LazyList?

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

相关推荐
服装学院的IT男15 分钟前
【Android 13源码分析】Activity生命周期之onCreate,onStart,onResume-2
android
Arms20618 分钟前
android 全面屏最底部栏沉浸式
android
服装学院的IT男21 分钟前
【Android 源码分析】Activity生命周期之onStop-1
android
ChinaDragonDreamer3 小时前
Kotlin:2.0.20 的新特性
android·开发语言·kotlin
网络研究院5 小时前
Android 安卓内存安全漏洞数量大幅下降的原因
android·安全·编程·安卓·内存·漏洞·技术
凉亭下5 小时前
android navigation 用法详细使用
android
小比卡丘8 小时前
C语言进阶版第17课—自定义类型:联合和枚举
android·java·c语言
前行的小黑炭9 小时前
一篇搞定Android 实现扫码支付:如何对接海外的第三方支付;项目中的真实经验分享;如何高效对接,高效开发
android
落落落sss10 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
代码敲上天.11 小时前
数据库语句优化
android·数据库·adb