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

相关推荐
懒人村杂货铺2 小时前
Android BLE 扫描完整实战
android
TeleostNaCl4 小时前
如何安装 Google 通用的驱动以便使用 ADB 和 Fastboot 调试(Bootloader)设备
android·经验分享·adb·android studio·android-studio·android runtime
fatiaozhang95275 小时前
中国移动浪潮云电脑CD1000-系统全分区备份包-可瑞芯微工具刷机-可救砖
android·网络·电脑·电视盒子·刷机固件·机顶盒刷机
2501_915918416 小时前
iOS 开发全流程实战 基于 uni-app 的 iOS 应用开发、打包、测试与上架流程详解
android·ios·小程序·https·uni-app·iphone·webview
lichong9516 小时前
【混合开发】vue+Android、iPhone、鸿蒙、win、macOS、Linux之dist打包发布在Android工程asserts里
android·vue.js·iphone
Android出海6 小时前
Android 15重磅升级:16KB内存页机制详解与适配指南
android·人工智能·新媒体运营·产品运营·内容运营
一只修仙的猿6 小时前
毕业三年后,我离职了
android·面试
编程乐学7 小时前
安卓非原创--基于Android Studio 实现的新闻App
android·ide·android studio·移动端开发·安卓大作业·新闻app
雅雅姐8 小时前
Android14 init.rc中on boot阶段操作4
android
fatiaozhang95278 小时前
中国移动中兴云电脑W132D-RK3528-2+32G-刷机固件包(非原机制作)
android·xml·电脑·电视盒子·刷机固件·机顶盒刷机