You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Compose:如何每秒局部刷新列表项的最后活跃时间

解决Jetpack Compose中LazyItem每秒局部刷新最后活跃时间的问题

问题根源

你当前的代码无法持续触发Toast,核心原因是Compose仅在状态值发生实际变化时才会触发重组:userActiveTime被反复设置为true,但布尔值没有发生变更,所以第一次设置后,后续的状态更新不会触发任何重组逻辑,Toast自然无法持续弹出。

正确解决方案

要实现LazyItem中「最后活跃时间」的每秒局部刷新,需要用持续变化的状态值触发重组,同时针对每个列表项的时间计算做优化,避免全列表重绘。

完整实现代码

首先定义用户数据结构:

data class User(
    val id: Int,
    val name: String,
    val lastActiveTimestamp: Long // 存储用户最后活跃的时间戳(毫秒)
)

然后实现列表与列表项:

@Composable
fun UserProfileList(users: List<User>) {
    // 用递增计数器触发每秒重组,替代无变化的布尔值
    val refreshCounter = remember { mutableIntStateOf(0) }

    LaunchedEffect(Unit) {
        while (true) {
            delay(1000)
            refreshCounter.intValue += 1 // 每次递增确保状态变化,触发重组
        }
    }

    LazyColumn {
        items(users) { user ->
            UserItem(user = user)
        }
    }
}

@Composable
fun UserItem(user: User) {
    // 用derivedStateOf计算相对活跃时间,仅在依赖变化时重新计算
    val lastActiveDisplayText = remember {
        derivedStateOf {
            val currentTime = System.currentTimeMillis()
            val timeDiff = currentTime - user.lastActiveTimestamp

            when {
                timeDiff < 1000 -> "刚刚"
                timeDiff < 60 * 1000 -> "${timeDiff / 1000}秒前"
                timeDiff < 60 * 60 * 1000 -> "${timeDiff / (60 * 1000)}分钟前"
                else -> "${timeDiff / (60 * 60 * 1000)}小时前"
            }
        }
    }

    Row(
        modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 12.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        // 头像占位
        Box(
            modifier = Modifier.size(48.dp).background(Color.Gray, CircleShape)
        )
        Spacer(modifier = Modifier.width(12.dp))
        Column(modifier = Modifier.weight(1f)) {
            Text(text = user.name, fontSize = 16.sp, fontWeight = FontWeight.Medium)
            Spacer(modifier = Modifier.height(4.dp))
            Text(
                text = "最后活跃: ${lastActiveDisplayText.value}",
                fontSize = 12.sp,
                color = Color.Gray
            )
        }
    }
}

关键优化点

  • 用计数器触发重组:mutableIntStateOf的计数器每秒递增,确保状态值持续变化,触发Compose重组
  • 局部刷新优化:derivedStateOf会自动跟踪依赖(间接依赖refreshCounter的变化触发计算),且LazyColumn的items只会重绘需要更新的UserItem,不会刷新整个列表
  • 时间计算逻辑隔离:把相对时间计算放在列表项内部,避免全局状态污染

原代码的快速修正(仅用于测试验证)

如果只是想验证每秒触发逻辑,可将Toast移至LaunchedEffect协程内(避免在重组中重复创建),同时改用计数器触发状态变化:

@Composable
fun TestRefreshTrigger() {
    val context = LocalContext.current
    val refreshCounter = remember { mutableIntStateOf(0) }

    LaunchedEffect(Unit) {
        while (true) {
            delay(1000)
            refreshCounter.intValue += 1
            // 在协程内弹出Toast,避免重组重复执行
            Toast.makeText(context, "called ${System.currentTimeMillis()}", Toast.LENGTH_SHORT).show()
        }
    }

    // 显示刷新次数,验证重组触发
    Text(text = "已刷新${refreshCounter.intValue}次", modifier = Modifier.padding(16.dp))
}

内容的提问来源于stack exchange,提问作者arun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 01:21:06