先用人话理解
这份小应用用数据类表达待办,用 remember 保存输入和列表状态,用 LazyColumn 显示任务。你可以添加、完成/撤销和删除任务。为了把重点放在 Compose 状态上,数据目前只在运行期间保存在内存中,关闭应用后不会保留。
本课要点:此示例的列表状态只保存在内存中,关闭应用后不会持久化。正式应用可进一步学习 ViewModel、Room 与状态提升。
代码例子
先读注释,再关注每一行如何把数据变成结果。
ANDROID · MainActivity.kt
// MainActivity.kt:使用 Android Studio 的 Empty Activity (Compose) 模板
// 若项目包名不同,请把第一行 package 改成项目实际包名。
package com.example.todo
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
data class Todo(val id: Int, val title: String, val done: Boolean = false)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { MaterialTheme { TodoScreen() } }
}
}
@Composable
fun TodoScreen() {
var nextId by remember { mutableIntStateOf(3) }
var input by remember { mutableStateOf("") }
var todos by remember { mutableStateOf(listOf(Todo(1, "学习 Kotlin"), Todo(2, "练习 Compose"))) }
Column(Modifier.fillMaxSize().padding(20.dp)) {
Text("待办清单", style = MaterialTheme.typography.headlineMedium)
OutlinedTextField(
value = input,
onValueChange = { input = it },
label = { Text("写下一个待办") },
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
Button(
onClick = {
val title = input.trim()
if (title.isNotEmpty()) {
todos = todos + Todo(nextId++, title)
input = ""
}
},
enabled = input.isNotBlank()
) { Text("添加任务") }
LazyColumn { items(todos, key = { it.id }) { todo ->
Row(Modifier.fillMaxWidth().padding(vertical = 8.dp), horizontalArrangement = Arrangement.SpaceBetween) {
Text(if (todo.done) "✓ ${todo.title}" else todo.title, modifier = Modifier.weight(1f))
TextButton(onClick = { todos = todos.map { if (it.id == todo.id) it.copy(done = !it.done) else it } }) { Text(if (todo.done) "撤销" else "完成") }
TextButton(onClick = { todos = todos.filterNot { it.id == todo.id } }) { Text("删除") }
}
} }
}
}再看一个用法
COMPOSE · 状态更新片段
// 状态更新示例:只修改被点击的那一条,其他待办保持不变
fun toggleTodo(todos: List<Todo>, selectedId: Int): List<Todo> =
todos.map { item ->
if (item.id == selectedId) item.copy(done = !item.done) else item
}动手改一改
代码阅读路线: 1. Todo 是数据模型,id 用于稳定识别每一项。 2. remember 保存当前界面状态;添加任务时创建新列表。 3. LazyColumn 只按需组合可见项目。 4. map + copy 只更新被点击的任务,其他任务保持不变。 练习:输入“喝水”并添加;再试试完成、撤销和删除这项任务,观察列表状态如何变化。
查看 Android / Compose 环境指南 ↗提示:Android 专属示例需要 Android Studio 项目和对应依赖;普通 Kotlin 示例可在 Playground 中尝试。
运行要求:请在 Android Studio 中创建或打开启用了 Jetpack Compose 的 Empty Activity 项目。把示例 package 行改成项目实际包名;Compose 依赖由项目模板配置。此代码不能直接粘贴进普通 Kotlin Playground。
三道小测
先自己选择,再展开答案。答错也没关系,重要的是知道为什么。
1. Todo 数据类主要表达什么?
查看答案与解析
正确答案:A。回到概念和代码示例,再解释一次为什么。
2. 为什么列表项使用稳定的 id?
查看答案与解析
正确答案:A。回到概念和代码示例,再解释一次为什么。
3. remember 保存的数据会自动写入数据库吗?
查看答案与解析
正确答案:B。抓住题目中的核心概念,重新对照示例理解。
深入阅读
想继续往下挖,可以阅读 Kotlin / Android 官方文档。
打开官方文档 ↗标记学习进度
进度保存在当前浏览器的 localStorage 中。