如何测量可组合物?
How to measure composables?
我有一个可组合按钮,它可以根据状态显示文本或加载程序
enum class State { NORMAL, LOADING }
@Composable
fun MyButton(onClick: () -> Unit, text: String, state: State) {
Button(onClick, Modifier.height(60.dp)) {
if (state == State.NORMAL) {
Text(text, fontSize = 32.sp)
} else {
CircularProgressIndicator(color = Color.Yellow)
}
}
}
我是这样使用它的:
MyButton(
onClick = {
state = if (state == State.NORMAL) State.LOADING else State.NORMAL
},
"hello",
state
)
但是按钮在进入loading
状态时缩小了。我怎样才能在 normal
状态下测量它,以便稍后在 loading
状态下分配这个测量宽度?
您可以创建一个名为 size 的变量(可为空)。只需将文本包装在 BoxWithConstraints
中,然后将大小分配给变量。通过重组记住这个值,你应该很好。
您可以在按钮显示后检查它的宽度并保存以备后用,这里有一个使用这种方法的可行解决方案。
@Composable
fun MyButton(onClick: () -> Unit, text: String, state: State) {
val currentContext= LocalContext.current
var textWidth by remember{ mutableStateOf(0)}
val textModifier= if(textWidth ==0)Modifier else Modifier.width(textWidth.dp)
Button(onClick, textModifier.height(60.dp).onGloballyPositioned {
textWidth= it.size.width.toDp(currentContext)
}) {
if (state == State.NORMAL) {
Text(text, fontSize = 32.sp)
} else {
CircularProgressIndicator(color = Color.Yellow)
}
}
}
fun Int.toDp(context: Context): Int = (this / context.resources.displayMetrics.density).toInt()
假设正常状态出现在加载状态之前,可以使用onGloballyPositioned
修饰符得到Text
的大小并应用到CircularProgressIndicator
.
类似于:
@Composable
fun MyButton(onClick: () -> Unit, text: String, state: State) {
var sizeText by remember { mutableStateOf(IntSize.Zero) }
Button(onClick, Modifier.height(60.dp)) {
if (state == State.NORMAL) {
Text(text, fontSize = 32.sp,
modifier = Modifier.onGloballyPositioned {
sizeText = it.size
})
} else {
Box(Modifier.size(with(LocalDensity.current){ (sizeText.width).toDp()},with(LocalDensity.current){ (sizeText.height).toDp()}),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(color = Color.Yellow)
}
}
}
}
您可以使用自定义布局 - 这在您从加载或正常开始时都有效:
@Composable
fun CustomContainer(
modifier: Modifier = Modifier,
state: State,
content: @Composable () -> Unit,
) {
Layout(
modifier = modifier,
content = content,
) { measurables, constraints ->
check(measurables.size == 2) { "This composable requires 2 children" }
val first = measurables[0]
val second = measurables[1]
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
val firstPlaceable = first.measure(looseConstraints)
val secondPlaceable = second.measure(looseConstraints)
val requiredWidth = max(firstPlaceable.width, secondPlaceable.width)
val requiredHeight = max(firstPlaceable.height, secondPlaceable.height)
layout(
requiredWidth,
requiredHeight
) {
when (state) {
State.LOADING -> {
firstPlaceable.place(
x = (requiredWidth - firstPlaceable.width) / 2,
y = (requiredHeight - firstPlaceable.height) / 2,
)
}
State.NORMAL -> {
secondPlaceable.place(
x = (requiredWidth - secondPlaceable.width) / 2,
y = (requiredHeight - secondPlaceable.height) / 2,
)
}
}
}
}
}
@Preview
@Composable
fun CustomButtonPreview() {
SampleTheme {
var state by remember {
mutableStateOf(State.LOADING)
}
Button(
onClick = {
state = when (state) {
State.NORMAL -> State.LOADING
State.LOADING -> State.NORMAL
}
},
Modifier.height(60.dp)
) {
CustomContainer(state = state) {
CircularProgressIndicator(color = Color.Yellow)
Text("Text here", fontSize = 32.sp)
}
}
}
}
我有一个可组合按钮,它可以根据状态显示文本或加载程序
enum class State { NORMAL, LOADING }
@Composable
fun MyButton(onClick: () -> Unit, text: String, state: State) {
Button(onClick, Modifier.height(60.dp)) {
if (state == State.NORMAL) {
Text(text, fontSize = 32.sp)
} else {
CircularProgressIndicator(color = Color.Yellow)
}
}
}
我是这样使用它的:
MyButton(
onClick = {
state = if (state == State.NORMAL) State.LOADING else State.NORMAL
},
"hello",
state
)
但是按钮在进入loading
状态时缩小了。我怎样才能在 normal
状态下测量它,以便稍后在 loading
状态下分配这个测量宽度?
您可以创建一个名为 size 的变量(可为空)。只需将文本包装在 BoxWithConstraints
中,然后将大小分配给变量。通过重组记住这个值,你应该很好。
您可以在按钮显示后检查它的宽度并保存以备后用,这里有一个使用这种方法的可行解决方案。
@Composable
fun MyButton(onClick: () -> Unit, text: String, state: State) {
val currentContext= LocalContext.current
var textWidth by remember{ mutableStateOf(0)}
val textModifier= if(textWidth ==0)Modifier else Modifier.width(textWidth.dp)
Button(onClick, textModifier.height(60.dp).onGloballyPositioned {
textWidth= it.size.width.toDp(currentContext)
}) {
if (state == State.NORMAL) {
Text(text, fontSize = 32.sp)
} else {
CircularProgressIndicator(color = Color.Yellow)
}
}
}
fun Int.toDp(context: Context): Int = (this / context.resources.displayMetrics.density).toInt()
假设正常状态出现在加载状态之前,可以使用onGloballyPositioned
修饰符得到Text
的大小并应用到CircularProgressIndicator
.
类似于:
@Composable
fun MyButton(onClick: () -> Unit, text: String, state: State) {
var sizeText by remember { mutableStateOf(IntSize.Zero) }
Button(onClick, Modifier.height(60.dp)) {
if (state == State.NORMAL) {
Text(text, fontSize = 32.sp,
modifier = Modifier.onGloballyPositioned {
sizeText = it.size
})
} else {
Box(Modifier.size(with(LocalDensity.current){ (sizeText.width).toDp()},with(LocalDensity.current){ (sizeText.height).toDp()}),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(color = Color.Yellow)
}
}
}
}
您可以使用自定义布局 - 这在您从加载或正常开始时都有效:
@Composable
fun CustomContainer(
modifier: Modifier = Modifier,
state: State,
content: @Composable () -> Unit,
) {
Layout(
modifier = modifier,
content = content,
) { measurables, constraints ->
check(measurables.size == 2) { "This composable requires 2 children" }
val first = measurables[0]
val second = measurables[1]
val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)
val firstPlaceable = first.measure(looseConstraints)
val secondPlaceable = second.measure(looseConstraints)
val requiredWidth = max(firstPlaceable.width, secondPlaceable.width)
val requiredHeight = max(firstPlaceable.height, secondPlaceable.height)
layout(
requiredWidth,
requiredHeight
) {
when (state) {
State.LOADING -> {
firstPlaceable.place(
x = (requiredWidth - firstPlaceable.width) / 2,
y = (requiredHeight - firstPlaceable.height) / 2,
)
}
State.NORMAL -> {
secondPlaceable.place(
x = (requiredWidth - secondPlaceable.width) / 2,
y = (requiredHeight - secondPlaceable.height) / 2,
)
}
}
}
}
}
@Preview
@Composable
fun CustomButtonPreview() {
SampleTheme {
var state by remember {
mutableStateOf(State.LOADING)
}
Button(
onClick = {
state = when (state) {
State.NORMAL -> State.LOADING
State.LOADING -> State.NORMAL
}
},
Modifier.height(60.dp)
) {
CustomContainer(state = state) {
CircularProgressIndicator(color = Color.Yellow)
Text("Text here", fontSize = 32.sp)
}
}
}
}