前言
上一课学习了 TextField 文本输入,掌握字符串状态、 value 与 onValueChange 的单向数据流。
本课学习两大常用能力:if条件渲染(根据状态决定组件是否显示)、LazyColumn懒加载列表(长列表只渲染屏幕可见项,性能高效)。
前置:完成1~6课,掌握状态、可组合函数、基础布局
一、完整可运行示例源码
kotlin
package com.example.composefirst
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ListDemoPage()
}
}
}
@Composable
fun ListDemoPage() {
// 输入文本状态
var inputText by remember { mutableStateOf("") }
// 待办列表,字符串集合
var todoList by remember { mutableStateOf(mutableListOf<String>()) }
Column(modifier = Modifier.padding(20.dp)) {
TextField(
value = inputText,
onValueChange = { inputText = it },
label = { Text("输入待办事项") }
)
Spacer(modifier = Modifier.height(12.dp))
Button(onClick = {
if(inputText.isNotBlank()){
todoList.add(inputText)
inputText = ""
}
}) {
Text("添加待办")
}
Spacer(modifier = Modifier.height(16.dp))
// if条件渲染:列表为空时显示提示文字
if(todoList.isEmpty()){
Text("暂无待办,请添加任务")
}else{
// LazyColumn 懒加载列表
LazyColumn {
items(todoList){ item ->
Text(text = "· $item", modifier = Modifier.padding(vertical = 4.dp))
}
}
}
}
}
@Preview
@Composable
fun ListPreview() {
ListDemoPage()
}
二、逐行代码解读
- if条件渲染
kotlin
if(todoList.isEmpty()){
Text("暂无待办,请添加任务")
}else{
LazyColumn { ... }
}
Compose可组合函数内部可以直接使用Kotlin的 if 判断。
- 条件成立:渲染if分支内的组件;
- 条件不成立:渲染else分支组件;
原理:状态变化触发重组,重新执行函数,根据条件动态选择渲染哪一段UI。
注意: if 必须返回同类型的组件树,不能返回不同类型。
- LazyColumn 懒加载纵向列表
Column 会一次性渲染全部子项,数据量大时卡顿。
LazyColumn 是懒加载容器:只渲染当前屏幕可见的条目,滑出屏幕的条目会被回收,适合长列表。
- items(集合) :批量遍历集合,为集合中每一项生成UI。
- 对比: Column 一次性加载全部; LazyColumn 按需加载。
- 待办列表状态
kotlin
var todoList by remember { mutableStateOf(mutableListOf<String>()) }
mutableListOf 可变集合,但是注意:修改集合内部元素,不会触发重组。
本示例 todoList.add() 可以刷新界面,是因为mutableStateOf会监听这个list引用变化;
易错点:如果只是修改list内部元素,引用不变,Compose不会感知,推荐使用 mutableStateListOf 专门做列表状态。
- 添加按钮逻辑
点击按钮,判断输入不为空,添加到列表,清空输入框;状态变更触发重组,列表自动刷新。
三、补充知识点:mutableStateListOf(推荐列表状态)
上面示例的优化写法,专门的可观察列表,修改内部元素也能触发重组:
kotlin
var todoList = remember { mutableStateListOf<String>() }
直接调用 todoList.add() 、 todoList.remove() ,自动通知Compose刷新界面,开发列表优先使用这个。
四、综合练习题
填空题
- Compose中实现根据状态动态展示UI,可直接使用Kotlin的________语句。
- 适合大量数据、按需渲染的纵向列表组件是________。
- LazyColumn遍历集合生成列表项,使用________函数。
答案与解析
- if ;解析:Compose是函数式UI,直接在代码内用if做条件渲染。
- LazyColumn ;解析:懒加载列表,只渲染屏幕可见条目,适合长数据。
- items ;解析:LazyColumn内部DSL,遍历集合生成每一行UI。
判断题
- Column适合上万条数据列表,性能和LazyColumn一样。()
答案:错误。理由:Column一次性渲染所有子元素,大量数据会造成页面卡顿;LazyColumn懒加载回收视图。
- if条件渲染,条件改变后,Compose会触发重组自动切换UI。()
答案:正确。理由:状态变更触发重组,重新执行可组合函数,if判断重新执行,切换界面。
- mutableStateListOf 列表,调用add添加元素,会自动触发界面重组刷新。()
答案:正确。理由:mutableStateListOf是Compose专用可观察列表,增删元素会通知框架刷新UI。
简答题
- 简述 Column 和 LazyColumn 的核心区别,各自适用场景?
参考答案:
Column一次性渲染全部子组件,适合少量固定布局内容;
LazyColumn懒加载,仅渲染屏幕可见条目,条目滑出屏幕回收,适合数量不确定、大量数据的滚动列表。
代码实操题
修改代码:给每一个待办项增加删除按钮,点击删除对应条目。参考答案
kotlin
@Composable
fun ListDemoPage() {
var inputText by remember { mutableStateOf("") }
val todoList = remember { mutableStateListOf<String>() }
Column(modifier = Modifier.padding(20.dp)) {
TextField(
value = inputText,
onValueChange = { inputText = it },
label = { Text("输入待办事项") }
)
Spacer(modifier = Modifier.height(12.dp))
Button(onClick = {
if(inputText.isNotBlank()){
todoList.add(inputText)
inputText = ""
}
}) {
Text("添加待办")
}
Spacer(modifier = Modifier.height(16.dp))
if(todoList.isEmpty()){
Text("暂无待办,请添加任务")
}else{
LazyColumn {
items(todoList){ item ->
Row(modifier = Modifier.padding(vertical = 4.dp)) {
Text("· $item")
Spacer(modifier = Modifier.width(10.dp))
Button(onClick = { todoList.remove(item) }) {
Text("删除")
}
}
}
}
}
}
}
答案与解析
解析:每行套Row横向布局,增加删除按钮;点击后调用 remove 删除列表元素,mutableStateListOf自动触发重组刷新列表。
五、本课小结
- if条件渲染:在可组合函数中直接分支渲染UI;
- LazyColumn:滚动懒加载列表,大数据优先选用;
- items 遍历集合生成列表项;
- 列表状态优先使用 mutableStateListOf ,增删自动触发重组。
下一课:rememberSaveable,页面旋转/系统重建保留状态。