一、列表和动画
列表和动画在应用内随处可见。在本课中,您将学习如何利用 Compose 轻松创建列表并添加有趣的动画效果。
1、创建消息列表
只包含一条消息的聊天略显孤单,因此我们将更改对话,使其包含多条消息。您需要创建一个可显示多条消息的 Conversation 函数。对于此用例,请使用 Compose 的 LazyColumn 和 LazyRow。这些可组合项只会呈现屏幕上显示的元素,因此,对于较长的列表,使用它们会非常高效。
@Composable
fun Conversation(messages: List<Message>) {
LazyColumn {
items(messages.size) { index ->
MessageCard(messages[index])
}
}
}
@Preview(showBackground = true)
@Composable
fun PreviewConversation() {
Compose_DemoTheme {
Conversation(messages = SampleData.conversationSample)
}
}
object SampleData{
val conversationSample = listOf(
Message("Lexi","Test...Test,,,Test,,,"),
Message("Lexi","只包含一条消息的聊天略显孤单。"),
Message("Lexi","因此我们将更改对话,使其包含多条消息。您需要创建一个可显示多条消息的 Conversation 函数"),
Message("Lexi","对于此用例,请使用 Compose 的 LazyColumn 和 LazyRow。这些可组合项只会呈现屏幕上显示的元素,因此,对于较长的列表,使用它们会非常高效"),
Message("Lexi","在此代码段中"),
Message("Lexi","您可以看到 LazyColumn 包含一个 items 子项"),
Message("Lexi","它接受 List 作为参数,并且其 lambda 会收到我们命名为 message 的参数(可以随意为其命名),该参数是 Message 的实例"),
Message("Lexi","简而言之,系统会针对提供的 List 的每个项调用此 lambda"),
Message("Lexi","将示例数据集复制到您的项目中,以便快速引导对话"),
)
}
在此代码段中,您可以看到 LazyColumn 包含一个 items 子项。它接受 List 作为参数,并且其 lambda 会收到我们命名为 index的参数(可以随意为其命名),该参数是 List的index。简而言之,系统会针对提供的 List 的每个项调用此 lambda。将示例数据集复制到您的项目中,以便快速引导对话。
2、在展开消息时显示动画效果
对话变得更加有趣了。是时候添加动画效果了!您将添加展开消息以显示更多内容的功能,同时为内容大小和背景颜色添加动画效果。为了存储此本地界面状态,您需要跟踪消息是否已展开。为了跟踪这种状态变化,您必须使用 remember 和 mutableStateOf 函数。
可组合函数可以使用 remember 将本地状态存储在内存中,并跟踪传递给 mutableStateOf 的值的变化。该值更新时,系统会自动重新绘制使用此状态的可组合项(及其子项)。这称为重组。
通过使用 Compose 的状态 API(如 remember 和 mutableStateOf),系统会在状态发生任何变化时自动更新界面。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// enableEdgeToEdge()
setContent {
Compose_DemoTheme {
Conversation(messages = SampleData.conversationSample)
}
}
}
}
@Composable
fun Conversation(messages: List<Message>) {
LazyColumn {
items(messages.size) { index ->
MessageCard(messages[index])
}
}
}
@Composable
fun MessageCard(msg: Message) {
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(id = R.drawable.ic_android_logo),
contentDescription = "Contact profile picture",
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
)
Spacer(modifier = Modifier.width(8.dp))
//We keep track if the message is expanded or not in this
//variable
var isExpanded by remember { mutableStateOf(false) }
Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
Text(
text = msg.author,
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.titleSmall
)
Spacer(modifier = Modifier.width(4.dp))
Surface(shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp) {
Text(
text = msg.body,
modifier = Modifier.padding(all = 4.dp),
// If the message is expanded, we display all its content
// otherwise we only display the first line
maxLines = if (isExpanded) Int.MAX_VALUE else 1,
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
注意:您需要添加以下导入内容才能正确使用 Kotlin 的委托属性语法(by 关键字)。按 Alt+Enter 或 Option+Enter 即可添加这些内容。
import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue
3、更改消息内容的背景颜色
现在,您可以根据点击消息时消息的 isExpanded 状态,更改消息内容的背景颜色。您将使用 clickable 修饰符来处理可组合项上的点击事件。您会为背景颜色添加动画效果,使其值逐步从 MaterialTheme.colorScheme.surface 更改为 MaterialTheme.colorScheme.primary(反之亦然),而不只是切换 Surface 的背景颜色。为此,您将使用 animateColorAsState 函数。最后,您将使用 animateContentSize 修饰符顺畅地为消息容器大小添加动画效果:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// enableEdgeToEdge()
setContent {
Compose_DemoTheme {
Conversation(messages = SampleData.conversationSample)
}
}
}
}
@Composable
fun Conversation(messages: List<Message>) {
LazyColumn {
items(messages.size) { index ->
MessageCard(messages[index])
}
}
}
@Composable
fun MessageCard(msg: Message) {
Row(modifier = Modifier.padding(all = 8.dp)) {
Image(
painter = painterResource(id = R.drawable.ic_android_logo),
contentDescription = "Contact profile picture",
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape)
)
Spacer(modifier = Modifier.width(8.dp))
//We keep track if the message is expanded or not in this
//variable
var isExpanded by remember { mutableStateOf(false) }
val surfaceColor by animateColorAsState(
if (isExpanded) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface,
)
Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) {
Text(
text = msg.author,
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.titleSmall
)
Spacer(modifier = Modifier.width(4.dp))
Surface(shape = MaterialTheme.shapes.medium,
shadowElevation = 1.dp,
color = surfaceColor,
modifier = Modifier.animateContentSize().padding(1.dp)
) {
Text(
text = msg.body,
modifier = Modifier.padding(all = 4.dp),
// If the message is expanded, we display all its content
// otherwise we only display the first line
maxLines = if (isExpanded) Int.MAX_VALUE else 1,
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
二、后续步骤
恭喜,您已完成 Compose 教程!您已高效地构建了一个简单的聊天界面,该界面显示包含图片和文字的可展开动画消息列表,使用 Material Design 原则设计,添加了深色主题且具有预览功能,所有内容只需不到 100 行代码!
以下是您目前为止所学的内容:
- 定义可组合函数
- 在可组合项中添加不同的元素
- 使用布局可组合项构建界面组件
- 使用修饰符扩展可组合项
- 创建高效列表
- 跟踪状态以及修改状态
- 在可组合项上添加用户互动
- 在展开消息时显示动画效果
Compose官方中文课程