如何测量可组合物?

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)
            }
        }
    }
}