其他
一篇长文,带你通透Compose的重组作用域和性能优化
https://blog.csdn.net/lyabc123456?type=blog
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
Text("Text1", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { counter++ },
shape = RoundedCornerShape(5.dp)
) {
Text("Text2: counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
}
fun getRandomColor() = Color(
red = Random.nextInt(256),
green = Random.nextInt(256),
blue = Random.nextInt(256),
alpha = 255
)
private fun Sample1() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
Text("Text1", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { counter++ },
shape = RoundedCornerShape(5.dp)
) {
MyText(counter)
}
}
}
@Composable
fun MyText(counter: Int) {
Column {
Text("MyText: counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Text("Another Text", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp)
) {
var update1 by remember { mutableStateOf(0) }
println("ROOT")
Text("Text in outer Column", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { update1++ },
shape = RoundedCornerShape(5.dp)
) {
println("🔥 Button 1")
Text(
text = "Text in Button1 read update1: $update1",
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier.background(getRandomColor())
)
}
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp)
) {
println("🚀 Inner Column")
var update2 by remember { mutableStateOf(0) }
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { update2++ },
shape = RoundedCornerShape(5.dp)
) {
println("✅ Button 2")
Text(
text = "Text in Button2 read update2: $update2",
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier.background(getRandomColor())
)
}
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(6.dp)
) {
println("☕ Bottom Column")
/**
* 🔥🔥 Observing update(mutableState) causes entire composable to recompose
*/
Text(
text = "🔥 Text in Inner Column read update1: $update1",
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier.background(getRandomColor())
)
}
}
}
}
inline fun Column(
modifier: Modifier = Modifier,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
content: @Composable ColumnScope.() -> Unit
) {
val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)
Layout(
content = { ColumnScopeInstance.content() },
measurePolicy = measurePolicy,
modifier = modifier
)
}
fun RandomColorColumn(content: @Composable () -> Unit) {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp)
) {
content()
}
}
fun Sample() {
RandomColorColumn {
var update1 by remember { mutableStateOf(0) }
Text("Text in outer Column", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { update1++ },
shape = RoundedCornerShape(5.dp)
) {
Text(
text = "Text in Button1 read update1: $update1",
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier.background(getRandomColor())
)
}
RandomColorColumn {
var update2 by remember { mutableStateOf(0) }
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { update2++ },
shape = RoundedCornerShape(5.dp)
) {
Text(
text = "Text in Button2 read update2: $update2",
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier.background(getRandomColor())
)
}
RandomColorColumn {
Text(
text = "🔥 Text in Inner Column read update1: $update1",
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier.background(getRandomColor())
)
}
}
}
}
之前在 Jetpack Compose中的绘制流程和自定义布局 中提到过,Compose 中不允许被多次测量,每个子元素只允许被测量一次,因此并不会因为嵌套层级的增加而导致测量次数的指数爆炸问题,正所谓 “一时嵌套一时爽,一直嵌套一直爽”。 实际上 Compose 编译器会对 Composable 函数施加一些 “魔法”,而 Compose runtime 会持有对这些 Composable 函数的引用,它们可能在运行时以任意顺序被重新执行、并行执行(可能多线程)、甚至被跳过执行,所以它们并不像我们传统意义上的标准函数调用堆栈那样,调用顺序也不会跟我们代码书写的那样按照先后顺序一层一层的往下调用再返回。
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
Text("Text1 Read counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { counter++ },
shape = RoundedCornerShape(5.dp)
) {
Text("Text2 Don't Read counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
}
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
val myData = remember { MyData() }
Text("Text1 Read counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
counter++
myData.value = myData.value + 1
},
shape = RoundedCornerShape(5.dp)
) {
Text("Text2 Read myData.value: ${myData.value}", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
}
class MyData(var value: Int = 0)
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
var myVal = 0
Text("Text1 Read counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
counter++
myVal++
},
shape = RoundedCornerShape(5.dp)
) {
Text("Text2 Read myVal: $myVal", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
}
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
var myVal = 0
Text("Text1 Read counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
counter++
myVal++
},
shape = RoundedCornerShape(5.dp)
) {
println(myVal)
Text("Text2 Read Nothing", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
}
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
var myVal = 0
Text("Text1 Read counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
counter++
myVal++
},
shape = RoundedCornerShape(5.dp)
) {
println("Hello World")
Text("Text2 Read Nothing", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
}
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
var myVal = 0
Text("Text1 Read counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
counter++
myVal++
},
shape = RoundedCornerShape(5.dp)
) {
Column {
Text("Text2 Read Nothing", color = Color.White, modifier = Modifier.background(getRandomColor()))
Text("Text3 Read myVal: $myVal", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
}
}
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = { counter++ },
) {
SomeComposable()
}
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
var myVal = 0
Text("Text1 Read counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
counter++
myVal++
},
shape = RoundedCornerShape(5.dp)
) {
Column {
SomeComposable()
Text("Text3 Read myVal: $myVal", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
}
}
@Composable
private fun SomeComposable() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 6.dp))
.background(getRandomColor())
.padding(4.dp)
) {
Text("Text2 Read Nothing", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
fun PhasesSample() {
var offsetX by remember { mutableStateOf(0f) }
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "OffsetX")
Spacer(modifier = Modifier.width(5.dp))
Slider(value = offsetX,
valueRange = 0f..50f,
onValueChange = {
offsetX = it
}
)
}
val modifier1 = Modifier
.offset(x = offsetX.dp) // 直接读
.layout { measurable, constraints ->
val placeable: Placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
println("😃️ modifier1 LAYOUT")
placeable.placeRelative(0, 0)
}
}
.background(Blue400)
.drawWithContent {
println("😜 modifier1 DRAW")
drawContent()
}
val modifier2 = Modifier
.offset { // 放在 lambda 中返回
val newX = offsetX.dp.roundToPx()
IntOffset(newX, 0)
}
.layout { measurable, constraints ->
val placeable: Placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
println("🍏 modifier2 LAYOUT")
placeable.placeRelative(0, 0)
}
}
.background(Blue400)
.drawWithContent {
println("🍎 modifier2 DRAW")
drawContent()
}
MyBox(modifier = modifier1, "modifier1")
Spacer(modifier = Modifier.height(8.dp))
MyBox(modifier = modifier2, "modifier2")
}
@Composable
private fun MyBox(modifier: Modifier, title: String) {
LogCompositions(msg = "🔥 MyBox() COMPOSITION $title")
Column(modifier) {
// This Text changes color in every recomposition
Text(
text = title,
color = Color.White,
modifier = Modifier
.background(getRandomColor())
.fillMaxWidth()
.padding(2.dp)
)
Text(
text = "modifier hash: ${modifier.hashCode()}\nModifier: $modifier",
color = Color.White,
modifier = Modifier.heightIn(max = 200.dp),
fontSize = 12.sp
)
}
}
.layout { measurable, constraints ->
val placeable: Placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
println("😃️ modifier1 LAYOUT")
// placeable.placeRelative(0, 0)
placeable.placeRelative(offsetX.dp.roundToPx(), 0) // 在这里设置
}
}
.background(Blue400)
.drawWithContent {
println("😜 modifier1 DRAW")
drawContent()
}
fun PhasesSample() {
var index by remember { mutableStateOf(0f) }
Text(text = "bgColor")
Slider(
value = index,
valueRange = 0f..100f,
onValueChange = { index = it }
)
val modifier1 = Modifier
.layout { measurable, constraints ->
val placeable: Placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
println("😃️ modifier1 LAYOUT")
placeable.placeRelative(0, 0)
}
}
.drawWithContent {
println("😜 modifier1 DRAW")
drawContent()
}
.background(randomColors[index.roundToInt()%randomColors.size]) // 直接设置
val modifier2 = Modifier
.layout { measurable, constraints ->
val placeable: Placeable = measurable.measure(constraints)
layout(placeable.width, placeable.height) {
println("🍏 modifier2 LAYOUT")
placeable.placeRelative(0, 0)
}
}
.drawWithContent { // 在 lambda 中设置
val color = randomColors[index.roundToInt()%randomColors.size]
println("🍎 modifier2 DRAW color: $color")
drawRect(color)
drawContent()
}
MyBox(modifier = modifier1, "modifier1")
Spacer(modifier = Modifier.height(20.dp))
MyBox(modifier = modifier2, "modifier2")
}
@Composable
private fun MyBox(modifier: Modifier, title: String) {
LogCompositions(msg = "🔥 MyBox() COMPOSITION $title")
Column(Modifier.background(purple500)) {
Text(
text = title,
color = Color.White,
modifier = modifier
.fillMaxWidth()
.padding(4.dp)
)
Text(
text = "modifier hash: ${modifier.hashCode()}\nModifier: $modifier",
color = Color.White,
fontSize = 12.sp
)
}
}
fun DeferredPaddingComposablesSample() {
var padding by remember { mutableStateOf(0f) }
//...
PaddingDeferred {
padding.dp
}
}
@Composable
private fun PaddingDeferred(padding: () -> Dp) {
Text(
text = "PaddingDeferred",
modifier = Modifier
.padding(start = padding())
.fillMaxWidth()
.background(getRandomColor())
)
}
fun DeferredPaddingComposablesSample() {
var padding by remember { mutableStateOf(0f) }
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Padding")
Spacer(modifier = Modifier.width(5.dp))
Slider(value = padding,
valueRange = 0f..50f,
onValueChange = {
padding = it
}
)
}
PaddingOuterDeferred { padding.dp } // 使用 lambda 传
PaddingOuterDirectly(padding.dp) // 直接传
}
@Composable
private fun PaddingOuterDeferred(padding: () -> Dp) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(getRandomColor())
.padding(10.dp)
) {
LogCompositions(msg = "😍 PaddingOuterDeferred")
Text("PaddingOuterDeferred")
PaddingMiddleDeferred(padding)
}
}
@Composable
private fun PaddingMiddleDeferred(padding: () -> Dp) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(getRandomColor())
.padding(10.dp)
) {
LogCompositions(msg = "😃 PaddingMiddleDeferred")
Text("PaddingMiddleDeferred")
PaddingInnerDeferred(padding)
}
}
@Composable
private fun PaddingInnerDeferred(padding: () -> Dp) {
LogCompositions(msg = "😜 PaddingInnerDeferred")
Text(
text = "PaddingInnerDeferred",
modifier = Modifier
.padding(start = padding())
.fillMaxWidth()
.background(getRandomColor())
)
}
@Composable
private fun PaddingOuterDirectly(padding: Dp) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(getRandomColor())
.padding(10.dp)
) {
LogCompositions(msg = "PaddingOuterDirectly")
Text("PaddingOuterDirectly")
PaddingMiddleDirectly(padding)
}
}
@Composable
private fun PaddingMiddleDirectly(padding: Dp) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(getRandomColor())
.padding(10.dp)
) {
LogCompositions(msg = "PaddingMiddleDirectly")
Text("PaddingMiddleDirectly")
PaddingInnerDirectly(padding)
}
}
@Composable
private fun PaddingInnerDirectly(padding: Dp) {
LogCompositions(msg = "PaddingInnerDirectly")
Text(
text = "PaddingInnerDirectly",
modifier = Modifier
.padding(start = padding)
.fillMaxWidth()
.background(getRandomColor())
)
}
@Stable
fun Modifier.padding(paddingValues: PaddingValues) =
this.then(
PaddingValuesModifier(
paddingValues = paddingValues,
inspectorInfo = debugInspectorInfo {
name = "padding"
properties["paddingValues"] = paddingValues
}
)
)
private class PaddingValuesModifier(
val paddingValues: PaddingValues,
inspectorInfo: InspectorInfo.() -> Unit
) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
require(
paddingValues.calculateLeftPadding(layoutDirection) >= 0.dp &&
paddingValues.calculateTopPadding() >= 0.dp &&
paddingValues.calculateRightPadding(layoutDirection) >= 0.dp &&
paddingValues.calculateBottomPadding() >= 0.dp
) {
"Padding must be non-negative"
}
val horizontal = paddingValues.calculateLeftPadding(layoutDirection).roundToPx() +
paddingValues.calculateRightPadding(layoutDirection).roundToPx()
val vertical = paddingValues.calculateTopPadding().roundToPx() +
paddingValues.calculateBottomPadding().roundToPx()
val placeable = measurable.measure(constraints.offset(-horizontal, -vertical))
val width = constraints.constrainWidth(placeable.width + horizontal)
val height = constraints.constrainHeight(placeable.height + vertical)
return layout(width, height) {
placeable.place(
paddingValues.calculateLeftPadding(layoutDirection).roundToPx(),
paddingValues.calculateTopPadding().roundToPx()
)
}
}
override fun hashCode() = paddingValues.hashCode()
override fun equals(other: Any?): Boolean {
val otherModifier = other as? PaddingValuesModifier ?: return false
return paddingValues == otherModifier.paddingValues
}
}
private fun Modifier.padding(paddings: Density.() -> PaddingValues) = then(
PaddingValuesModifier(paddings, rtlAware = true)
)
private class PaddingValuesModifier(
val paddings: Density.() -> PaddingValues,
val rtlAware: Boolean,
) : LayoutModifier {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
val paddingValues = paddings()
require(
paddingValues.calculateLeftPadding(layoutDirection) >= 0.dp &&
paddingValues.calculateTopPadding() >= 0.dp &&
paddingValues.calculateRightPadding(layoutDirection) >= 0.dp &&
paddingValues.calculateBottomPadding() >= 0.dp
) {
"Padding must be non-negative"
}
val horizontal = paddingValues.calculateLeftPadding(layoutDirection).roundToPx() +
paddingValues.calculateRightPadding(layoutDirection).roundToPx()
val vertical = paddingValues.calculateTopPadding().roundToPx() +
paddingValues.calculateBottomPadding().roundToPx()
val placeable = measurable.measure(constraints.offset(-horizontal, -vertical))
val width = constraints.constrainWidth(placeable.width + horizontal)
val height = constraints.constrainHeight(placeable.height + vertical)
return layout(width, height) {
if (rtlAware) {
placeable.placeRelative(
paddingValues.calculateLeftPadding(layoutDirection).roundToPx(),
paddingValues.calculateTopPadding().roundToPx()
)
} else {
placeable.place(
paddingValues.calculateLeftPadding(layoutDirection).roundToPx(),
paddingValues.calculateTopPadding().roundToPx()
)
}
}
}
override fun hashCode() : Int {
var result = paddings.hashCode()
result = 31 * result + rtlAware.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
val otherModifier = other as? PaddingValuesModifier ?: return false
return paddings == otherModifier.paddings &&
rtlAware == otherModifier.rtlAware
}
}
@Composable
private fun PaddingInnerDeferred(padding: () -> Dp) {
Text(
text = "PaddingInnerDeferred",
modifier = Modifier
//.padding(start = padding())
.padding {
PaddingValues(start = padding())
}
.fillMaxWidth()
.background(getRandomColor())
)
}
var imageHeightPx by remember { mutableStateOf(0) }
Image(
painter = painterResource(R.drawable.rectangle),
contentDescription = "I'm above the text",
modifier = Modifier
.fillMaxWidth()
.onSizeChanged { size ->
// Don't do this
imageHeightPx = size.height
}
)
Text(
text = "I'm below the image",
modifier = Modifier.padding(
top = with(LocalDensity.current) { imageHeightPx.toDp() }
)
)
}
Modifier.onSizeChanged()、onGloballyPositioned() 或一些其他布局操作 更新某种状态 使用该状态作为对布局修饰符(padding()、height() 或类似元素)的输入 可能会重复
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
val myData = remember { MyData(0) }
Text("Text1 Read counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
counter++
myData.value = myData.value + 1
},
shape = RoundedCornerShape(5.dp)
) {
MyText(myData.value)
}
}
}
class MyData(var value: Int = 0)
@Composable
fun MyText(counter: Int = 0) {
Column {
Text("MyText: Read myData.value: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Text("Another Text", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
fun Sample() {
Column(
modifier = Modifier
.padding(4.dp)
.shadow(1.dp, shape = CutCornerShape(topEnd = 8.dp))
.background(getRandomColor())
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
var counter by remember { mutableStateOf(0) }
var myData = remember { MyData1(0) }
Text("Text1 Read counter: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Button(
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
onClick = {
counter++
myData = MyData1(myData.value + 1)
},
shape = RoundedCornerShape(5.dp)
) {
MyText(myData.value)
}
}
}
class MyData1(val value: Int = 0)
@Composable
fun MyText(counter: Int = 0) {
Column {
Text("MyText: Read myData.value: $counter", color = Color.White, modifier = Modifier.background(getRandomColor()))
Text("Another Text", color = Color.White, modifier = Modifier.background(getRandomColor()))
}
}
@Stable
interface UiState<T> {
val value T?
val exception: Throwable?
val hasError: Boolean
get() = exception != null
}
fun WelcomeView(user: UnStableUser) {
Text(text = "Welcome ${user.username}!")
}
data class UnStableUser(
var id: String,
var email: String,
var username: String
)
val id: String,
val email: String,
val username: String
)
fun UnStableUser.toUser(): User {
return User(
id = id,
email = email,
username = username
)
}
fun User.toUnStableUser(): ExternalUser {
return UnStableUser(
id = id,
email = email,
username = username
)
}
fun SomeLayout(onClick: () -> Unit) {
Button(onClick = onClick) {
Text(text = "text")
}
}
fun SomeComposable() {
SomeLayout {
// ...
}
}
override fun invoke() {
// ...
}
}
fun MainScreen(viewModel: MainViewModel = viewModel()) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CoffeeSelector(
type = viewModel.type,
onTypeChange = {
viewModel.changeType(it)
}
)
}
}
@Composable
fun CoffeeSelector(
type: Type,
onTypeChange: (Type) -> Unit,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier.padding(10.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.size(100.dp)
.background(getRandomColor()),
contentAlignment = Alignment.Center
) {
val text = when(type) {
Type.BIG -> "大杯"
Type.MIDDLE -> "中杯"
Type.SMALL -> "小杯"
}
Text(text = text, fontSize = 16.sp, color = Color.White)
}
Spacer(modifier = Modifier.height(16.dp))
Column {
MyButton("大杯") {
onTypeChange(Type.BIG)
}
MyButton("中杯") {
onTypeChange(Type.MIDDLE)
}
MyButton("小杯") {
onTypeChange(Type.SMALL)
}
}
}
}
@Composable
fun MyButton(text: String, onClick: () -> Unit) {
Button(
onClick = onClick,
colors = ButtonDefaults.buttonColors(backgroundColor = getRandomColor()),
modifier = Modifier.fillMaxWidth()
) {
Text(
text = text,
fontSize = 16.sp,
color = Color.White,
modifier = Modifier.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
)
}
}
enum class Type { BIG, MIDDLE, SMALL }
var type by mutableStateOf(Type.MIDDLE)
private set
fun changeType(type: Type) {
this.type = type
}
}
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MainScreen(viewModel)
}
}
}
fun MainScreen(viewModel: MainViewModel = viewModel()) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CoffeeSelector(
type = viewModel.type,
onTypeChange = viewModel::changeType
)
}
}
fun MainScreen(viewModel: MainViewModel = viewModel()) {
val changeTypeLambda = remember<(Type) -> Unit> {
{
viewModel.changeType(it)
}
}
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CoffeeSelector(
type = viewModel.type,
onTypeChange = changeTypeLambda
)
}
}
fun MainScreen(viewModel: MainViewModel = viewModel()) {
var type by remember { mutableStateOf(Type.MIDDLE) }
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CoffeeSelector(
type = type,
onTypeChange = {
type = it
}
)
}
}
fun ScrollingList() {
var listState = rememberLazyListState();
LazyRow {...}
// Here!
Log.d(TAG, "List recompose ${listState.firstVisibleItemIndex}")
MyComposable(offset = listState.firstVisibleItemIndex)
}
@Composable
fun MyComposable(offset : Int) {...}
fun ScrollingList() {
var listState = rememberLazyListState();
LazyRow {...}
// 使用SideEffect处理副作用
SideEffect {
Log.d(TAG, "List recompose ${listState.firstVisibleItemIndex}")
}
MyComposable(offset = listState.firstVisibleItemIndex)
}
@Composable
fun MyComposable(offset : Int) {...}
fun String.Log(block: () -> String) {
if (BuildConfig.DEBUG) return
SideEffect {
Log.d(this, block())
}
}
fun ScrollingList() {
var listState = rememberLazyListState();
LazyRow {...}
TAG.Log { "List recompose ${listState.firstVisibleItemIndex}" }
MyComposable(offset = listState.firstVisibleItemIndex)
}
.gradlew -Pandroidx.enableComposeCompilerMetrics=true :compose:runtime:runtime:compileKotlin
.gradlew -Pandroidx.enableComposeCompilerReports=true :compose:runtime:runtime:compileKotlin
kotlinOptions {
jvmTarget = '1.8'
// Enable Compose Compiler Report
freeCompilerArgs += ["-P","plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
project.buildDir.absolutePath + "/compose_metrics"]
// Enable Compose Compiler Metrics
freeCompilerArgs += ["-P","plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
project.buildDir.absolutePath + "/compose_metrics"]
}
}
xxx-module-module.json:其中包含了一些整体的统计数据。 xxx-module-composables.txt:其中包含了每个Composable函数声明的详细输出。 xxx-module-composables.csv:这是对应上面txt文件的表格版本。 xxx-module-composables.log:其中会包含一些日志信息。 xxx-module-classes.txt:其中包含了每个Composable函数引用的类的稳定性的相关信息。
手动修改该函数的参数类型,确保所有参数都是 stable 的类型 使用注解 @NonRestartableComposable 将函数标注为不可重启
unstable val user: TraktUser?
stable val authState: TraktAuthState
stable val isLoading: Boolean
stable val isEmpty: Boolean
stable val selectionOpen: Boolean
unstable val selectedShowIds: Set<Long>
stable val filterActive: Boolean
stable val filter: String?
unstable val availableSorts: List<SortOption>
stable val sort: SortOption
unstable val message: UiMessage?
<runtime stability> = Unstable
}
公开的字段是可变的 (例如使用var修饰的属性) 公开的字段是另一个不稳定的类型
加载并启动应用。 在启动后立即显示应用的空白启动窗口。 创建应用进程。
创建 Application 对象。 启动主线程消息循环。 创建主 Activity。 填充视图内容。 进行View布局。 执行初始绘制。