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

Jetpack Compose:LazyColumn新项滑入动画及消息场景实现方案问询

Jetpack Compose LazyColumn 新项滑入动画实现

一、普通正序LazyColumn的新项滑入动画

直接给列表项添加滑入+淡入的进入动画,用官方的animateEnterExit修饰符即可实现,比自定义layout更稳定适配LazyColumn的复用机制:

@Composable
fun NormalLazyColumnWithAnimation() {
    val items = remember { mutableStateListOf<String>() }
    LaunchedEffect(Unit) {
        repeat(5) {
            delay(500)
            items.add("新消息 ${items.size + 1}")
        }
    }

    LazyColumn(modifier = Modifier.fillMaxSize()) {
        items(items) { item ->
            Text(
                text = item,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp)
                    .animateEnterExit(
                        enter = slideInHorizontally(initialOffsetX = { it }) + fadeIn(),
                        exit = slideOutHorizontally(targetOffsetX = { -it }) + fadeOut()
                    )
            )
        }
    }
}

slideInHorizontally(initialOffsetX = { it })表示从右侧滑入(it为当前组件宽度),配合fadeIn()实现淡入效果,新项添加时自动触发动画。

二、reverseLayout=true的聊天场景动画

聊天场景下LazyColumn开启倒序排列后,需配合布局排列调整,同时保持新消息从右侧滑入的视觉逻辑:

@Composable
fun ChatLazyColumnWithAnimation() {
    val messages = remember { mutableStateListOf<Message>() }
    val sendMessage = { content: String ->
        messages.add(Message(content, isMine = true))
    }

    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        reverseLayout = true,
        verticalArrangement = Arrangement.Top // 配合倒序,让新消息显示在屏幕底部
    ) {
        items(messages) { message ->
            ChatBubble(
                message = message,
                modifier = Modifier
                    .animateEnterExit(
                        enter = slideInHorizontally(initialOffsetX = { it }) + fadeIn(),
                        exit = slideOutHorizontally(targetOffsetX = { it }) + fadeOut()
                    )
            )
        }
    }

    // 底部发送按钮
    Box(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
        Button(onClick = { sendMessage("我的消息 ${messages.size + 1}") }) {
            Text("发送消息")
        }
    }
}

@Composable
fun ChatBubble(message: Message, modifier: Modifier = Modifier) {
    val alignment = if (message.isMine) Alignment.End else Alignment.Start
    Box(
        modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp),
        contentAlignment = alignment
    ) {
        Surface(
            color = if (message.isMine) Color(0xFFDCF8C6) else Color.White,
            shape = RoundedCornerShape(16.dp),
            modifier = modifier.padding(8.dp)
        ) {
            Text(
                text = message.content,
                modifier = Modifier.padding(12.dp),
                fontSize = 16.sp
            )
        }
    }
}

data class Message(val content: String, val isMine: Boolean)

关键注意点:

  • 开启reverseLayout=true后必须设置verticalArrangement = Arrangement.Top,否则新消息会出现在屏幕顶部,不符合聊天场景的视觉习惯。
  • 滑入方向保持从右侧触发即可,因为聊天气泡的位置由父布局的对齐控制,和列表的倒序排列逻辑无关。

关于之前方案的问题

AnimatedVisibility配合modifier.layout()需要手动处理布局偏移,很容易和LazyColumn的组件回收复用机制冲突,导致动画异常。而animateEnterExit是官方针对列表项进出优化的API,自动适配LazyColumn的内部逻辑,动画更流畅稳定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 07:57:03