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

相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui