如何用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

相关推荐
2501_916008895 小时前
Web 前端开发常用工具推荐与团队实践分享
android·前端·ios·小程序·uni-app·iphone·webview
我科绝伦(Huanhuan Zhou)6 小时前
MySQL一键升级脚本(5.7-8.0)
android·mysql·adb
怪兽20147 小时前
Android View, SurfaceView, GLSurfaceView 的区别
android·面试
龚礼鹏8 小时前
android 图像显示框架二——流程分析
android
消失的旧时光-19438 小时前
kmp需要技能
android·设计模式·kotlin
帅得不敢出门8 小时前
Linux服务器编译android报no space left on device导致失败的定位解决
android·linux·服务器
雨白9 小时前
协程间的通信管道 —— Kotlin Channel 详解
android·kotlin
TimeFine11 小时前
kotlin协程 容易被忽视的CompletableDeferred
android
czhc114007566312 小时前
Linux1023 mysql 修改密码等
android·mysql·adb
GOATLong13 小时前
MySQL内置函数
android·数据库·c++·vscode·mysql