英文:
Change button text without changing the button width?
问题
当saveInProgress
设置为true时,Button
的文本变短,Button
会重新调整大小。我想让Button
保持原始大小(不设置固定大小)。我该如何做到这一点?
英文:
Consider the following code:
val saveInProgress = false // in reality might come from a ViewModel
Button(onClick = {}) {
val text = if(saveInProgress) "Save" else "..."
Text(text)
}
When saveInProgress
is set to true, the Button
text gets shorter and the Button
resizes. I want the Button
to keep it's original size though (without setting a fixed size). How can I do that?
答案1
得分: 2
你可以设置 Modifier.widthIn()
,以确保按钮的最小宽度不小于指定的值。
例如:
val saveInProgress = false
Button(
onClick = {},
modifier = Modifier.widthIn(min = 80.dp)
) {
val text = if (saveInProgress) "Save" else "..."
Text(text)
}
英文:
You can set Modifier.widthIn()
, so that the button's minimum width is not less than the specified value.
For example:
val saveInProgress = false
Button(
onClick = {},
modifier = Modifier.widthIn(min = 80.dp)
) {
val text = if (saveInProgress) "Save" else "..."
Text(text)
}
答案2
得分: 2
在 Jetpack 版本 1.4.x 中,引入了 rememberTextMeasurer 用于测量文本。通过测量文本,您可以在将任何内容布局在 Composable 中之前就获得宽度。
val text = "Initial Text Size"
val density = LocalDensity.current
val textMeasurer: TextMeasurer = rememberTextMeasurer()
val style: TextStyle = MaterialTheme.typography.button
val initialWidthDp: Dp = remember(text) {
with(density) {
textMeasurer.measure(
text = text,
style= style,
).size.width.toDp()
}
}
按钮的样式是必需的,以正确测量文本。
@Preview
@Composable
private fun TextWidthSample() {
val text = "Initial Text Size"
val density = LocalDensity.current
val textMeasurer: TextMeasurer = rememberTextMeasurer()
val style: TextStyle = MaterialTheme.typography.button
val initialWidthDp: Dp = remember(text) {
with(density) {
textMeasurer.measure(
text = text,
style = style,
).size.width.toDp()
}
}
var isLoading by remember {
mutableStateOf(false)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(20.dp)
) {
Button(
onClick = { /*TODO*/ }
) {
Text(
modifier = Modifier
.width(initialWidthDp),
text = if (isLoading) "Loading" else text,
textAlign = TextAlign.Center
)
}
Spacer(modifier = Modifier.height(20.dp))
Button(
onClick = { isLoading = isLoading.not() }
) {
Text("Loading: $isLoading")
}
}
}
如果版本低于 1.4.0 并且不想使用实验性 API,可以使用 Modifier.onSizeChanged 与另一个重新组合或 SubcomposeLayout,而无需另一个重新组合。可以使用 SubcomposeLayout 来获取 Composable 的尺寸,即使在测量和布局之前,也可以像在 Button 内添加进度按钮一样进行操作。
英文:
In Jetpack Version 1.4.x rememberTextMeasurer is introduced to measure text. By measuring your text you can get width even before laying outt anything in a Composable as
val text = "Initial Text Size"
val density = LocalDensity.current
val textMeasurer: TextMeasurer = rememberTextMeasurer()
val style: TextStyle = MaterialTheme.typography.button
val initialWidthDp: Dp = remember(text) {
with(density) {
textMeasurer.measure(
text = text,
style= style,
).size.width.toDp()
}
}
Style of button is required to measure text correctly.
Full sample
@Preview
@Composable
private fun TextWidthSample() {
val text = "Initial Text Size"
val density = LocalDensity.current
val textMeasurer: TextMeasurer = rememberTextMeasurer()
val style: TextStyle = MaterialTheme.typography.button
val initialWidthDp: Dp = remember(text) {
with(density) {
textMeasurer.measure(
text = text,
style = style,
).size.width.toDp()
}
}
var isLoading by remember {
mutableStateOf(false)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(20.dp)
) {
Button(
onClick = { /*TODO*/ }
) {
Text(
modifier = Modifier
.width(initialWidthDp),
text = if (isLoading) "Loading" else text,
textAlign = TextAlign.Center
)
}
Spacer(modifier = Modifier.height(20.dp))
Button(
onClick = { isLoading = isLoading.not() }
) {
Text("Loading: $isLoading")
}
}
}
If version is lower than 1.4.0 and don't want to use experimental api you can use Modifier.onSizeChanged with another recomposition or SubcomposeLayout without another recomposition. SubcomposeLayout can be used to get dimensions of Composable even before it's measured and laid out like adding progress button inside Button while loading.
答案3
得分: 1
以下是翻译的内容:
var maxWidth by remember { mutableStateOf(0) }
Button(
modifier = Modifier
.onSizeChanged { maxWidth = maxOf(maxWidth, it.width) }
.then(if (maxWidth > 0) Modifier.width(maxWidth) else Modifier)
) {}
fun Modifier.invisible(isInvisible: Boolean) = drawWithContent {
if (!isInvisible) {
drawContent()
}
}
Button {
Box {
Text("保存", Modifier.invisible(saveInProgress))
Text("...", Modifier.invisible(!saveInProgress))
}
}
英文:
There are multiple solutions to this, you can for example use Modifier.onSizeChanged
on the Button to keep track of max width and then apply that width. This will make sure that the Button never becomes smaller. Something like this:
var maxWidth by remember { mutableStateOf(0) }
Button(
modifier = Modifier
.onSizeChanged { maxWidth = maxOf(maxWidth, it.width) }
.then(if (maxWidth > 0) Modifier.width(maxWidth) else Modifier)
) {}
But this is not ideal if your second text is gonna be longer, so maybe better solution is to put both texts inside the button and make one of them invisible, like this:
fun Modifier.invisible(isInvisible: Boolean) = drawWithContent {
if (!isInvisible) {
drawContent()
}
}
Button {
Box {
Text("Save", Modifier.invisible(saveInProgress))
Text("...", Modifier.invisible(!saveInProgress))
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论