• Compose实战-实现一个带下拉加载更多功能的LazyColumn


    前言:

    LazyColumn类似于android原生中的listview,用来加载列表中的数据。

    本文主要记录如何使用compose去实现一个带下拉加载更多功能的列表。

    一.使用LazyColumn实现基本列表功能

    首先构建ComposeListActivity,用来装载界面。

    主要包含以下几块:

    list:mutableStateListOf类型,可被观测状态的数据源。

    ListGreeting:LazyColumn绘制的方法,展示list中的数据。

    ListMessageCard:类似于ItemView,用来显示每一行的数据

    loadMore:模拟加载更多数据的方法。

    代码如下:

    1. /**
    2. * @author lxl
    3. * compose带下拉加载更多功能的lazyColumn
    4. */
    5. class ComposeListActivity : ComponentActivity() {
    6. private val list = mutableStateListOf();
    7. override fun onCreate(savedInstanceState: Bundle?) {
    8. super.onCreate(savedInstanceState)
    9. loadMore()
    10. setContent {
    11. DemoClientTheme {
    12. // A surface container using the 'background' color from the theme
    13. ListGreeting()
    14. }
    15. }
    16. }
    17. @SuppressLint("UnrememberedMutableState")
    18. @Composable
    19. fun ListGreeting() {
    20. Log.i("lxltest","ListGreeting")
    21. val state = rememberLazyListState()
    22. Row(
    23. Modifier
    24. .padding(5.dp)
    25. .fillMaxWidth(),
    26. horizontalArrangement = Arrangement.Center
    27. ) {
    28. LazyColumn(Modifier.padding(5.dp), state = state) {
    29. items(list) { name ->
    30. ListMessageCard(name)
    31. }
    32. }
    33. LoadMoreListHandler(listState = state) {
    34. Toast.makeText(this@ComposeListActivity, "加载中,请稍后", Toast.LENGTH_SHORT).show()
    35. loadMore()
    36. }
    37. }
    38. }
    39. @Composable
    40. fun ListMessageCard(name: String) {
    41. Row(modifier = Modifier.padding(all = 12.dp)) {
    42. Spacer(modifier = Modifier.width(10.dp))
    43. Column {
    44. Text(
    45. text = name,
    46. color = MaterialTheme.colors.secondaryVariant,
    47. style = MaterialTheme.typography.subtitle2,
    48. fontSize = 16.sp
    49. )
    50. Spacer(modifier = Modifier.width(12.dp))
    51. }
    52. }
    53. }
    54. private fun loadMore() {
    55. val start = list.size
    56. Log.i("lxltest", "加载start:${start}${start + 30}")
    57. list.apply {
    58. for (i in 0 until 30) {
    59. add("text${start + i}")
    60. }
    61. }
    62. }
    63. }

    二.使用remember记录状态

    compose区别于传统的安卓布局,compose是并不是使用回调的概念,而使用的一种观察者的模式。并且默认是无状态的,所以我们要使用remember来记录状态并设置到LazyColumn中去。

    首先生成一个loadMore的续体,如果loadMore的状态发生了变化,则会进行回调。而状态是否发生变化,则是我们上面的remember中的判断。

    我们这里判断如果距离底部还有1个时触发状态的变化,返回true。

    1. val loadMore = remember {
    2. derivedStateOf {
    3. val layoutInfo = listState.layoutInfo
    4. val totalItemsCount = layoutInfo.totalItemsCount
    5. val lastVisibleItemIndex = (layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0) + 1
    6. val b = lastVisibleItemIndex > (totalItemsCount - buffer)
    7. Log.i(
    8. "lxltest",
    9. "lastVisibleItemIndex:${lastVisibleItemIndex},totalItemsCount:${totalItemsCount},buffer:${buffer},result:${b}"
    10. )
    11. b
    12. }
    13. }

    然后把loadMore的续体传入LaunchedEffect中,这时候如果loadMore.value的状态发生变化,则会调用传进来的onLoaderMore进行通知

    1. LaunchedEffect(loadMore)
    2. {
    3. snapshotFlow { loadMore.value }
    4. .distinctUntilChanged()
    5. .collect {
    6. onLoaderMore()
    7. }
    8. }

    最完这些工作后,发现果然是可以了,拉拉到30,60临界点时,会自动加载新的数据。

    但是也发现一个问题,就是每次加载数据,不是加载30条,而是60条。这是为何?

    三.排查解决问题

    在collect中加入日志辅助排查,终于找到了问题。原来loadMore状态变化通知时,会通知两次,第一次是状态发生变化的通知,此时it=true。有一次it=false,我怀疑有可能是修改完成的一个回调。具体没有去追踪,所以这里简单做一下it判断就可以了。完整代码如下:

    1. @Composable
    2. fun LoadMoreListHandler(listState: LazyListState, buffer: Int = 1, onLoaderMore: () -> Unit) {
    3. val loadMore = remember {
    4. derivedStateOf {
    5. val layoutInfo = listState.layoutInfo
    6. val totalItemsCount = layoutInfo.totalItemsCount
    7. val lastVisibleItemIndex = (layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0) + 1
    8. val b = lastVisibleItemIndex > (totalItemsCount - buffer)
    9. Log.i(
    10. "lxltest",
    11. "lastVisibleItemIndex:${lastVisibleItemIndex},totalItemsCount:${totalItemsCount},buffer:${buffer},result:${b}"
    12. )
    13. b
    14. }
    15. }
    16. LaunchedEffect(loadMore)
    17. {
    18. snapshotFlow { loadMore.value }
    19. .distinctUntilChanged()
    20. .collect {
    21. if (it) {
    22. onLoaderMore()
    23. }
    24. }
    25. }
    26. }

    四.项目地址

    完整代码地址:

    GitHub - aa5279aa/android_all_demo: 一直觉得研究各种技术,一个个demo的下载运行太费劲了,为什么不能有一个所有新技术的融合体demo呢?项目为此而生

  • 相关阅读:
    小程序随机生成文字卡片文案海报,带分享保存
    pytorch基础操作(1)
    网易云信4K 8K RTC助力远程医疗的技术实践
    C++ 中 const 成员函数的本质
    响应式设计的实现方式
    程序员对代码注释可以说是又爱又恨又双标
    一次不规范HTTP请求引发的nginx响应400问题分析与解决
    java版直播商城平台规划及常见的营销模式 电商源码/小程序/三级分销+商城免费搭建
    flutter版本dart版本对应关系
    【Unity3D】动画回调函数、动画事件、动画曲线
  • 原文地址:https://blog.csdn.net/AA5279AA/article/details/126102777