前言
最近有新项目,要用到Compose开发,发现以前学的Compose也忘了很多了,看来好记性还是不如烂笔头,记录一下Compose相关的知识点,以后再忘了也能翻一翻看看。
一、Modifier 与独立参数:核心功能 vs 外在表现
核心结论
独立参数决定控件「能干嘛、承载什么数据」(核心本质),Modifier 只决定控件「长什么样、放在哪」(外在表现),二者无法互相替代。
详细讲解
把控件比作 "外卖员" 就能快速理解:
独立参数
外卖员的核心职责:比如要配送的餐品(对应TextField的value)、配送地址(对应Button的onClick)------ 没有这些,外卖员就失去了存在的意义;
Modifier
外卖员的外在形象:比如制服颜色(对应background)、身高体型(对应size)、配送路线偏移(对应padding)------ 这些变了,核心配送职责不会变。
开发场景举例
用Modifier.clickable替代Button的onClick:Button的onClick是核心参数,自带「点击按压态、禁用态视觉变灰、无障碍支持」等原生特性;而给Box加Modifier.clickable,虽然能实现 "点击触发逻辑",但没有任何Button的原生交互反馈(比如禁用后还是原来的样式),相当于 "普通人穿了外卖服,却不会送外卖"。
TextField的value/onValueChange:这两个参数是TextField的 "灵魂"------ 没有它们,TextField只是个空盒子,根本不算 "输入框";而Modifier只能给它加圆角、调宽度,永远替代不了 "输入文本并响应变化" 的核心功能。
kotlin
@Composable
fun DemoParamVsModifier() {
// 1. 有核心参数的Button:自带禁用态视觉反馈
Button(onClick = {}, enabled = false) { Text("禁用按钮") }
// 2. 仅加Modifier.clickable的Box:无禁用视觉反馈
Box(modifier = Modifier.clickable(enabled = false) {}) { Text("假按钮") }
// 3. TextField核心参数不可替代
var text by remember { mutableStateOf("") }
TextField(
value = text, // 核心数据(必传)
onValueChange = { text = it }, // 核心逻辑(必传)
modifier = Modifier.padding(10.dp) // 仅调整边距(外在)
)
}
二、Modifier 重复执行规则:指令链而非 "覆盖式配置"
核心结论
Modifier 不是 "键值对"(比如size=100dp被size=200dp覆盖),而是「按顺序执行的指令链」------ 布局类 Modifier 叠加生效,绘制类 Modifier 层叠覆盖。
详细讲解
把 Modifier 链比作 "给蛋糕做装饰的步骤":
布局类 Modifier(padding/size) :
padding:先给蛋糕围 1cm 奶油边(padding(10.dp)),再围 2cm 巧克力边(padding(20.dp)),最终边宽是 3cm(叠加);
size:普通size是 "建议尺寸"(先建议做 10cm 蛋糕,后建议做 20cm,最终按 20cm 做);requiredSize是 "强制尺寸"(比如强制做 8cm,不管之前建议多少,都按 8cm 来)。
绘制类 Modifier(background/border):先给蛋糕涂红色奶油(background(Color.Red)),再涂蓝色奶油(background(Color.Blue)),最终看到的是蓝色 ------ 因为绘制是 "顶层覆盖底层"。
开发场景举例
做商品卡片时,用两层padding实现 "外间距 + 内间距":
第一层padding(10.dp):让卡片和其他元素隔开(外间距);
第二层padding(5.dp):让卡片内容和边框隔开(内间距);
最终卡片内容整体内缩 15dp,这是 "叠加" 的核心价值,若用 "覆盖" 逻辑根本实现不了。
java
@Composable
fun DemoModifierOrder() {
// 1. padding叠加:最终布局尺寸=100+10+20=130dp
Box(modifier = Modifier.size(100.dp).padding(10.dp).padding(20.dp).background(Color.Red))
// 2. background覆盖:最终显示蓝色(后写的在顶层)
Box(modifier = Modifier.size(50.dp).background(Color.Red).background(Color.Blue))
// 3. requiredSize强制覆盖:最终尺寸80dp,忽略前序200dp
Box(modifier = Modifier.size(200.dp).requiredSize(80.dp).background(Color.Green))
}
三、自定义 Compose 组件:参数设计的核心规则
核心结论
自定义组件参数遵循「核心参数→非核心参数→Modifier」的顺序:核心参数强制传(无默认值),非核心参数设默认值,Modifier 放最后且默认值为Modifier。
详细讲解
自定义组件就像 "定制奶茶":
核心参数:奶茶品类(比如珍珠奶茶)、甜度(正常糖)------ 没有这些,商家不知道做什么奶茶,必须强制选;
非核心参数:加冰 / 去冰、加小料(默认不加)------ 可选,不选就按默认来;
Modifier:杯子大小(大杯 / 小杯)、包装(袋装 / 杯装)------ 最后选,不影响奶茶本身的口味(核心)。
关键注意点
必须把外部传入的Modifier传给组件根布局:比如自定义按钮的根布局是Button,要写modifier = 外部modifier + 内部样式(如modifier.height(48.dp)),否则外部传的Modifier会失效;
绝对不能把核心数据塞进 Modifier:比如用Modifier.productData(product)传递商品信息,既语义混乱(分不清是样式还是数据),又会导致 Modifier 频繁重建,性能变差。
java
// 自定义圆角按钮:参数设计规范
@Composable
fun RoundedButton(
// 1. 核心参数:强制传,放前面(无默认值)
text: String,
onClick: () -> Unit,
// 2. 非核心参数:设默认值,放中间
buttonColor: Color = Color.Blue,
// 3. Modifier:放最后,默认值为Modifier
modifier: Modifier = Modifier
) {
Button(
onClick = onClick,
// 组合外部Modifier + 内部核心样式(关键)
modifier = modifier.height(48.dp).clip(RoundedCornerShape(16.dp)),
colors = ButtonDefaults.buttonColors(containerColor = buttonColor)
) { Text(text) }
}
// 调用示例:核心参数必传,其他可选
@Composable
fun DemoCustomComponent() {
// 基础调用:仅传核心参数
RoundedButton(text = "基础按钮", onClick = {})
// 自定义调用:覆盖非核心参数+追加Modifier
RoundedButton(
text = "绿色按钮",
onClick = {},
buttonColor = Color.Green,
modifier = Modifier.size(200.dp) // 覆盖内部height(48.dp)
)
}
四、Compose 函数的本质:不只是加 @Composable 注解
核心结论
真 Compose 函数 = @Composable注解 + 返回值 Unit + 描述 UI + 支持响应式重组;仅加注解的是 "伪 Compose 函数",无实际 UI 意义。
详细讲解
把 Compose 函数比作 "动态画板":
@Composable注解 = 拿到画板的使用权限(必要但不够);
返回值 Unit = 不是画完把画板拿走,而是在画板上 "描述要画什么"(不能返回 "画好的按钮 / 文本");
描述 UI = 在画板上画按钮、文本(核心价值,没有这个就是 "空画板");
响应式重组 = 画板能自动更新 ------ 比如画的计数从 0 变 1,不用重新画整个画板,只更新数字部分(靠mutableStateOf实现)。
开发场景举例
伪 Compose 函数:加了@Composable,但只做1+1的计算,不画任何 UI;或画了 UI 但用普通变量val count=0,点击按钮数字永远不变(无重组能力);
真 Compose 函数:加注解、画文本 + 按钮,用remember { mutableStateOf(0) }定义计数,点击按钮数字自动更新(重组)。
java
// 1. 真Compose函数:支持响应式重组
@Composable
fun RealComposeFunc() {
var count by remember { mutableStateOf(0) } // 可观察状态
Column {
Text("计数:$count")
Button(onClick = { count++ }) { Text("加1") } // 点击更新状态,UI自动变
}
}
// 2. 伪Compose函数:无重组能力
@Composable
fun FakeComposeFunc() {
val count = 0 // 普通变量(非可观察状态)
Column {
Text("计数:$count") // 永远显示0
Button(onClick = {}) { Text("加1") } // 点击无效果
}
}