Val*_*kov 3 android android-layout android-jetpack android-jetpack-compose
Android Jetpack Compose 包含width(),height()和size()布局修饰符以及requiredWidth(),requiredHeight()和requiredSize()。这两组修饰符有什么区别?我应该使用普通修饰符还是必需的修饰符?
Thr*_*ian 23
在 jetpack Compose 中,可组合项或子可组合项的尺寸是使用约束来设置的,约束是一组尺寸和有界或有限的标志。Modifier.requiredX用于扩大约束或强制新的约束,但如果约束不在父级的限制中,则很容易破坏布局。
/**\n * Create a [Constraints]. [minWidth] and [minHeight] must be positive and\n * [maxWidth] and [maxHeight] must be greater than or equal to [minWidth] and [minHeight],\n * respectively, or [Infinity][Constraints.Infinity].\n */\n@Stable\nfun Constraints(\n minWidth: Int = 0,\n maxWidth: Int = Constraints.Infinity,\n minHeight: Int = 0,\n maxHeight: Int = Constraints.Infinity\n): Constraints {\n require(maxWidth >= minWidth) {\n "maxWidth($maxWidth) must be >= than minWidth($minWidth)"\n }\n require(maxHeight >= minHeight) {\n "maxHeight($maxHeight) must be >= than minHeight($minHeight)"\n }\n require(minWidth >= 0 && minHeight >= 0) {\n "minWidth($minWidth) and minHeight($minHeight) must be >= 0"\n }\n return Constraints.createConstraints(minWidth, maxWidth, minHeight, maxHeight)\n}\nRun Code Online (Sandbox Code Playgroud)\n基于Constraints测量期间的宽度和高度被分配给可组合项作为Placeables。大小修饰符在返回的测量函数内执行MeasureResult。
val placeable = measurable.measure(wrappedConstraints)\n return layout(placeable.width, placeable.height) {\n placeable.placeRelative(0, 0)\n }\nRun Code Online (Sandbox Code Playgroud)\n当我们分配任何大小修饰符时,我们实际上并没有分配我们传递的大小Constraints,而是根据Constraints可组合的最小-最大属性的范围在内部进行测量MeasureScope并放置在PlacementScope或layout()函数内部。
例如,在密度为 1.0 的设备上 Modifier.size(50.dp) 通过Constraints(minWidth = 50, maxWidth = 50, minHeight = 50, maxHewight= 50)。我们设置了一个固定范围Constraints来测量我们的可组合项,它只能有 50.dp 大小。
如果我们设置一个范围,Modifier.sizeIn例如Modifier.sizeIn(minWidth=50, maxWidth=100, minHeight=50, maxHeight=100)Composable 可以在 50.dp 和 100.dp 之间,因为我们分配了一个开放范围的约束。
当我们分配时,Modifier.size(50.dp).size(100.dp)我们将\n传递Constraint(50, 50, 50, 50)给具有目标约束的第二个大小修饰符Constraints(100, 100, 100, 100)。
Modifier.size/width/height/sizeIn/widthIn/heightIn和Modifier.requiredSize/SizeIn/width/WidthIn/HeightIn修饰符共享相同的源代码,但有细微差别,size修饰符检查
/**\n * Takes [otherConstraints] and returns the result of coercing them in the current constraints.\n * Note this means that any size satisfying the resulting constraints will satisfy the current\n * constraints, but they might not satisfy the [otherConstraints] when the two set of constraints\n * are disjoint.\n * Examples (showing only width, height works the same):\n * (minWidth=2, maxWidth=10).constrain(minWidth=7, maxWidth=12) -> (minWidth = 7, maxWidth = 10)\n * (minWidth=2, maxWidth=10).constrain(minWidth=11, maxWidth=12) -> (minWidth=10, maxWidth=10)\n * (minWidth=2, maxWidth=10).constrain(minWidth=5, maxWidth=7) -> (minWidth=5, maxWidth=7)\n */\nfun Constraints.constrain(otherConstraints: Constraints) = Constraints(\n minWidth = otherConstraints.minWidth.coerceIn(minWidth, maxWidth),\n maxWidth = otherConstraints.maxWidth.coerceIn(minWidth, maxWidth),\n minHeight = otherConstraints.minHeight.coerceIn(minHeight, maxHeight),\n maxHeight = otherConstraints.maxHeight.coerceIn(minHeight, maxHeight)\n)\nRun Code Online (Sandbox Code Playgroud)\n在Modifier.size/width/height/sizeIn/widthIn/heightIn修饰符分配上,此检查已完成,但为了简单起见,我将解释链中的第二个大小Modifier.size(50.dp).size(100.dp)
这里分配了 100.dp 的约束otherConstraints,min\n和 max 来自当前大小,在我们的例子中Modifier.size(50.dp),结果100.coerceIn(50, 50)返回 50,这就是使用第一个的原因。
If we chain Modifier.widthIn(50.dp, 100.dp).width(80.dp) it returns 80.dp\n\nIf we chain Modifier.widthIn(50.dp, 100.dp).width(120.dp) it returns 100.dp\n\nIf we chain Modifier.widthIn(50.dp, 100.dp).width(40.dp) returns 50.dp\nRun Code Online (Sandbox Code Playgroud)\n\n@Composable\nprivate fun ChainSizeModifiersSample() {\n TutorialText2(text = "\xe2\x9d\x8cfillMaxWidth().width(50.dp)")\n\n BoxWithConstraints(\n modifier = Modifier\n .fillMaxWidth()\n .width(50.dp)\n .border(2.dp, Color.Red)\n ) {\n Text(text = "minWidth: $minWidth, maxWidth: $maxWidth")\n }\n\n TutorialText2(text = "\xe2\x9d\x8cwidth(200).width(50.dp)")\n BoxWithConstraints(\n modifier = Modifier\n .width(200.dp)\n .width(50.dp)\n .border(2.dp, Color.Red)\n ) {\n Text(text = "minWidth: $minWidth, maxWidth: $maxWidth")\n }\n\n StyleableTutorialText(\n text = "2-) **Modifier.width/height/sizeIn** describes a range " +\n "between min and max values. It\'s allowed to narrow range but " +\n "not allowed to widen it as can be seen examples below."\n )\n\n TutorialText2(text = "\xe2\x9c\x85widthIn(min = 100.dp, max = 200.dp).width(150.dp)")\n BoxWithConstraints(\n modifier = Modifier\n .widthIn(min = 100.dp, max = 200.dp)\n .width(150.dp)\n .border(2.dp, Color.Green)\n ) {\n Text(text = "minWidth: $minWidth, maxWidth: $maxWidth")\n }\n\n TutorialText2(text = "\xe2\x9d\x8cwidthIn(min = 100.dp, max = 200.dp).width(50.dp)")\n BoxWithConstraints(\n modifier = Modifier\n .widthIn(min = 100.dp, max = 200.dp)\n .width(50.dp)\n .border(2.dp, Color.Red)\n ) {\n Text(text = "minWidth: $minWidth, maxWidth: $maxWidth")\n }\n\n TutorialText2(text = "\xe2\x9d\x8cwidthIn(min = 100.dp, max = 200.dp).width(250.dp)")\n BoxWithConstraints(\n modifier = Modifier\n .widthIn(min = 100.dp, max = 200.dp)\n .width(250.dp)\n .border(2.dp, Color.Red)\n ) {\n Text(text = "minWidth: $minWidth, maxWidth: $maxWidth")\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n即使requiredX修饰符共享相同的代码,它在返回时也有细微的Constraints差别
val resolvedMinWidth = if (minWidth != Dp.Unspecified) {\n targetConstraints.minWidth\n } else {\n constraints.minWidth.coerceAtMost(targetConstraints.maxWidth)\n }\n val resolvedMaxWidth = if (maxWidth != Dp.Unspecified) {\n targetConstraints.maxWidth\n } else {\n constraints.maxWidth.coerceAtLeast(targetConstraints.minWidth)\n }\n val resolvedMinHeight = if (minHeight != Dp.Unspecified) {\n targetConstraints.minHeight\n } else {\n constraints.minHeight.coerceAtMost(targetConstraints.maxHeight)\n }\n val resolvedMaxHeight = if (maxHeight != Dp.Unspecified) {\n targetConstraints.maxHeight\n } else {\n constraints.maxHeight.coerceAtLeast(targetConstraints.minHeight)\n }\nRun Code Online (Sandbox Code Playgroud)\n如果您注意源代码大小修饰符只能缩小约束范围,但requiredX修饰符也可以扩大它,基本上它们没有限制,例如size/width/height/sizeIn/widthIn/heightIn可以在当前约束之后设置任何目标约束。
Modifier.width(50.dp).requiredWidh(100.dp)将最小和最大 50.dp 更改为 100.dp,其中您的可组合项可以使用最小-最大 100.dp 进行测量,因此它会被分配 100.dp 宽度。
但是,如果不遵守父项或初始项的约束,则父项会将此视为 50.dp,它会尝试将内容居中,并根据requiredX修改器大于或小于初始size修改器的大小将该子项向左或向右推。
@Composable\nprivate fun ChainRequiredSizeModifierSample() {\n // In these examples requiredWidth constraints do not match the one comes from Modifier.size\n // Because of that parent attempts to place them in center. When required is bigger\n // it\'s placed at (parent.max-max) at left side. When required is smaller content is\n // place at right side(centered in parent).\n Column(\n modifier = Modifier\n .fillMaxWidth()\n .padding(20.dp)\n .border(2.dp, Blue400)\n ) {\n TutorialText2(text = "\xe2\x9d\x8csize(100.dp).requiredWidth(140.dp)")\n BoxWithConstraints(\n modifier = Modifier\n .border(2.dp, Color.Red)\n .size(100.dp)\n .requiredWidth(140.dp)\n ) {\n Text(\n text = "minWidth: $minWidth, maxWidth: $maxWidth",\n modifier = Modifier.border(3.dp, Color.Green)\n )\n }\n\n TutorialText2(text = "\xe2\x9d\x8csize(100.dp).requiredWidth(80.dp)")\n BoxWithConstraints(\n modifier = Modifier\n .border(2.dp, Color.Red)\n .size(100.dp)\n .requiredWidth(80.dp)\n ) {\n Text(\n text = "minWidth: $minWidth, maxWidth: $maxWidth",\n modifier = Modifier.border(3.dp, Color.Green)\n )\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n代码可以在这里找到
\n通常,Modifier.requiredX当您希望强制使用最小尺寸时,会使用变体,但您也可以使用它来强制最大尺寸。CheckBox 使用它强制 20.dp 大小
Canvas(modifier.wrapContentSize(Alignment.Center).requiredSize(CheckboxSize))\nRun Code Online (Sandbox Code Playgroud)\n如果您分配的大小修饰符小于 20.dp,您会发现它没有放置在应有的位置。滑块也同样如此
\n BoxWithConstraints(\n modifier\n .minimumInteractiveComponentSize()\n .requiredSizeIn(minWidth = ThumbRadius * 2, minHeight = ThumbRadius * 2)\nRun Code Online (Sandbox Code Playgroud)\n它们需要具有最小尺寸才能正确显示或绘制。如果我们分配的大小不是必需的,它会尝试居中,如上面的示例所示。
\n\n@Preview\n@Composable\nprivate fun RequiredSample() {\n Column(\n modifier = Modifier.fillMaxSize().padding(20.dp).border(2.dp, Color.Cyan)\n ) {\n Checkbox(checked = true, onCheckedChange = null, modifier = Modifier.size(20.dp))\n Spacer(Modifier.height(30.dp))\n Checkbox(checked = true, onCheckedChange = null, modifier = Modifier.size(10.dp))\n Spacer(Modifier.height(30.dp))\n Slider(\n value = 0f,\n onValueChange = {},\n modifier = Modifier.size(48.dp).border(2.dp, Color.Green)\n )\n Spacer(Modifier.height(30.dp))\n Slider(\n value = 0f,\n onValueChange = {},\n modifier = Modifier.size(20.dp).border(2.dp, Color.Green)\n )\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n返回固定约束或具有相等 minWidth/Height 和 maxWidth/Height 的约束。\n因此子可组合项仅限于此约束
\n覆盖父级最大约束下可用的空间部分。它返回父宽度/高度作为最小和最大宽度/高度
\n将最小宽度/高度保留为此值,以便可以使用此尺寸来测量子可组合项。子项可以低于此值或更大,直到最大宽度/高度
\n这是可以测量儿童的最大宽度/高度。子可组合项的测量尺寸不能大于最大宽度/高度。
\n
\n@Composable\nprivate fun ConstraintsSample1() {\n Text(text = "Fixed Size")\n BoxWithConstraints(modifier = Modifier\n .size(100.dp)\n .border(3.dp, Color.Green)) {\n Box(modifier = Modifier\n .size(50.dp)\n .background(Color.Red))\n }\n\n Spacer(modifier=Modifier.height(10.dp))\n BoxWithConstraints(modifier = Modifier\n .size(100.dp)\n .border(3.dp, Color.Green)) {\n Box(modifier = Modifier\n .size(150.dp)\n .background(Color.Red))\n }\n\n Text(text = "widthIn(min)")\n\n BoxWithConstraints(modifier = Modifier\n .widthIn(min = 100.dp)\n .border(3.dp, Color.Green)) {\n Box(modifier = Modifier\n .size(50.dp)\n .background(Color.Red))\n }\n\n Spacer(modifier=Modifier.height(10.dp))\n BoxWithConstraints(modifier = Modifier\n .widthIn(min = 100.dp)\n .border(3.dp, Color.Green)) {\n Box(modifier = Modifier\n .size(150.dp)\n .background(Color.Red))\n }\n\n\n Text(text = "widthIn(max)")\n\n BoxWithConstraints(modifier = Modifier\n .widthIn(max = 100.dp)\n .border(3.dp, Color.Green)) {\n Box(modifier = Modifier\n .size(50.dp)\n .background(Color.Red))\n }\n\n Spacer(modifier=Modifier.height(10.dp))\n BoxWithConstraints(modifier = Modifier\n .widthIn(max = 100.dp)\n .border(3.dp, Color.Green)) {\n Box(modifier = Modifier\n .size(150.dp)\n .background(Color.Red))\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n例如,当您设置Modifier.size(50.dp).size(100.dp)第一个尺寸修饰符时,但是如果您用户 requiredIn
Modifier.size(50.dp).requiredSize(100.dp) \nRun Code Online (Sandbox Code Playgroud)\n100.dp 但布局放置为两个维度之间差异的一半
\n
\n@Composable\nprivate fun ConstraintsSample2() {\n\n Column(modifier = Modifier\n .fillMaxSize()\n .padding(30.dp)\n .border(4.dp, Color.Cyan)) {\n\n Text(text = "Chaining size modifiers")\n\n Box(modifier = Modifier\n .size(50.dp)\n .background(Color.Yellow))\n\n Box(modifier = Modifier\n .size(50.dp)\n .size(100.dp)\n .background(Color.Red))\n\n Box(modifier = Modifier\n .size(50.dp)\n .requiredSizeIn(100.dp)\n .background(Color.Green))\n\n\n Text(text = "widthIn(max)")\n\n BoxWithConstraints(\n modifier = Modifier\n .width(100.dp)\n .border(3.dp, Color.Green)\n ) {\n Box(\n modifier = Modifier\n .requiredWidthIn(min = 20.dp, max = 50.dp)\n .height(50.dp)\n .background(Color.Red)\n )\n }\n\n Spacer(modifier = Modifier.height(10.dp))\n BoxWithConstraints(\n modifier = Modifier\n .width(100.dp)\n .border(3.dp, Color.Green)\n ) {\n Box(\n modifier = Modifier\n .requiredWidthIn(min = 150.dp, max = 200.dp)\n .height(50.dp)\n .background(Color.Red)\n )\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n在下面的示例中,当 TextField 没有固定大小的父级时,它会增长到最大宽度。如果使用 Modifier.widthIn() 时设置了小于 TextField 的固定大小,则 TextField 设置为父宽度,而 Modifier.requiredWidthIn() 跳出父级为 (TextField 大小 - 父级大小)/2
\n\n@Composable\nprivate fun TextFieldSamples() {\n\n Column(\n modifier = Modifier\n .padding(20.dp).border(2.dp, Color.Cyan)\n ) {\n\n var text1 by remember { mutableStateOf("") }\n\n Column(modifier = Modifier) {\n TextField(\n modifier = Modifier\n .border(2.dp, Color.Green)\n .widthIn(56.dp),\n value = text1,\n onValueChange = { text1 = it }\n )\n\n TextField(\n modifier = Modifier\n .border(2.dp, Color.Red)\n .requiredWidth(56.dp),\n value = text1, onValueChange = { text1 = it })\n }\n\n Spacer(modifier = Modifier.height(30.dp))\n\n var text2 by remember { mutableStateOf("") }\n\n Column(modifier = Modifier.width(50.dp)) {\n TextField(\n modifier = Modifier\n .border(2.dp, Color.Green)\n .widthIn(56.dp),\n value = text2, onValueChange = { text2 = it })\n\n TextField(\n modifier = Modifier\n .border(2.dp, Color.Red)\n .requiredWidth(56.dp),\n value = text2, onValueChange = { text2 = it })\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n约束根据尺寸修改器、垂直/水平滚动或父级选择限制或想要使用最小或最大尺寸的方式而变化。
\n我的设备上的最大设备宽度为 1080px,在下面的示例中 200.dp 为 525px。并使用了verticalScroll,这就是为什么高度是用Constraints.Infinity
\n@Composable\nfun ConstraintsSample3() {\n Column(modifier = Modifier) {\n\n Text(text = "No Dimension Modifier")\n\n BoxWithConstraints(modifier = Modifier.background(Brown400)) {\n val hasBoundedWidth = constraints.hasBoundedWidth\n val hasFixedWidth = constraints.hasFixedWidth\n val minWidth = constraints.minWidth\n val maxWidth = constraints.maxWidth\n\n val hasBoundedHeight = constraints.hasBoundedHeight\n val hasFixedHeight = constraints.hasFixedHeight\n val minHeight = constraints.minHeight\n val maxHeight = constraints.maxHeight\n Text(\n "minWidth: $minWidth, maxWidth: $maxWidth\\n" +\n "hasBoundedWidth: $hasBoundedWidth, hasFixedWidth: $hasFixedWidth\\n" +\n "minHeight: $minHeight, maxHeight: $maxHeight\\n" +\n "hasBoundedHeight: $hasBoundedHeight, hasFixedHeight: $hasFixedHeight",\n color = Color.White\n )\n }\n\n Spacer(modifier = Modifier.height(10.dp))\n Text(text = "FillMaxWidth and 200.dp Height")\n BoxWithConstraints(\n modifier = Modifier\n .fillMaxWidth()\n .height(200.dp)\n .background(Red400)\n ) {\n val hasBoundedWidth = constraints.hasBoundedWidth\n val hasFixedWidth = constraints.hasFixedWidth\n val minWidth = constraints.minWidth\n val maxWidth = constraints.maxWidth\n\n val hasBoundedHeight = constraints.hasBoundedHeight\n val hasFixedHeight = constraints.hasFixedHeight\n val minHeight = constraints.minHeight\n val maxHeight = constraints.maxHeight\n Text(\n "minWidth: $minWidth, maxWidth: $maxWidth\\n" +\n "hasBoundedWidth: $hasBoundedWidth, hasFixedWidth: $hasFixedWidth\\n" +\n "minHeight: $minHeight, maxHeight: $maxHeight\\n" +\n "hasBoundedHeight: $hasBoundedHeight, hasFixedHeight: $hasFixedHeight",\n color = Color.White\n )\n }\n\n Spacer(modifier = Modifier.height(10.dp))\n Text(text = "wrapContentSize()")\n BoxWithConstraints(\n modifier = Modifier\n .wrapContentSize()\n .background(Orange400)\n ) {\n\n val hasBoundedWidth = constraints.hasBoundedWidth\n val hasFixedWidth = constraints.hasFixedWidth\n val minWidth = constraints.minWidth\n val maxWidth = constraints.maxWidth\n\n val hasBoundedHeight = constraints.hasBoundedHeight\n val hasFixedHeight = constraints.hasFixedHeight\n val minHeight = constraints.minHeight\n val maxHeight = constraints.maxHeight\n Text(\n "minWidth: $minWidth, maxWidth: $maxWidth\\n" +\n "hasBoundedWidth: $hasBoundedWidth\\n" +\n "hasFixedWidth: $hasFixedWidth\\n" +\n "minHeight: $minHeight\\n" +\n "maxHeight: $maxHeight\\n" +\n "hasBoundedHeight: $ha
Val*_*kov 12
不同的是,普通的像改性宽度()考虑到布局约束而需要像改性剂requiredWidth()忽略它们。您可以将约束视为测量元素的最小/最大宽度/高度。为了更好地理解布局修饰符和约束是如何工作的,请查看这篇文章。
让我们看一个宽度修饰符的例子,高度和大小的行为方式相同。
@Composable
fun WidthModifierExample() {
val columnWidth = 200.dp
Column(
modifier = Modifier
.width(columnWidth)
.border(1.dp, Color.Gray)
) {
Text(
text = "requiredWidth = parent - 50",
modifier = Modifier
.requiredWidth(columnWidth - 50.dp)
.background(Color.LightGray)
)
Text(
text = "requiredWidth = parent + 50",
modifier = Modifier
.requiredWidth(columnWidth + 50.dp)
.background(Color.LightGray)
)
Text(
text = "width = parent - 50",
modifier = Modifier
.width(columnWidth - 50.dp)
.background(Color.LightGray),
)
Text(
text = "width = parent + 50",
modifier = Modifier
.width(columnWidth + 50.dp)
.background(Color.LightGray)
)
}
}
Run Code Online (Sandbox Code Playgroud)
深灰色边框为列边界,文本背景为浅灰色。Modifier.width(200.dp)应用于列,因此它接收minWidth = 200dp; maxWidth = 200dp约束。该列所以每个孩子得到的布局并不限制其孩子最小宽度minWidth = 0; maxWidth = 200dp的限制。前两个文本使用requiredWidth()修饰符忽略此约束,因此第二个文本比父列宽。最后一个文本宽度200dp根据maxWidth = 200dp约束减小,因为它使用width()修饰符代替。
在上面的示例中,仅限制了文本的最大宽度。让我们看另一个示例,其中最小宽度也受到限制:
@Composable
fun WidthModifierExample() {
Column(
modifier = Modifier.border(1.dp, Color.Gray)
) {
val minWidth = 140
val maxWidth = 200
val widthDescriptions = arrayOf(
WidthDescription(minWidth - 50, "min - 50"),
WidthDescription((minWidth + maxWidth) / 2, "between min and max"),
WidthDescription(maxWidth + 50, "max + 50")
)
for (widthDescription in widthDescriptions) {
Text(
text = "requiredWidth = ${widthDescription.description}",
modifier = Modifier
.border(.5.dp, Color.Red)
.widthIn(minWidth.dp, maxWidth.dp)
.background(Color.LightGray)
.requiredWidth(widthDescription.width.dp)
)
}
for (widthDescription in widthDescriptions) {
Text(
text = "width = ${widthDescription.description}",
modifier = Modifier
.border(.5.dp, Color.Red)
.widthIn(minWidth.dp, maxWidth.dp)
.background(Color.LightGray)
.width(widthDescription.width.dp)
)
}
}
}
data class WidthDescription(
val width: Int,
val description: String
)
Run Code Online (Sandbox Code Playgroud)
在这种情况下,Column()不添加任何约束,但每个子项Text()都包含在widthIn()修饰符中,该修饰符添加了自己的约束。浅灰色背景显示文本大小,而红色边框显示结果元素的尺寸。如您所见requiredWidth(),前三个文本的修饰符忽略了外部约束,因此元素宽度及其子元素Text()宽度可能不同。最后三个文本使用width()修饰符,它考虑了约束,因此Text()宽度始终与结果元素匹配。
| 归档时间: |
|
| 查看次数: |
4926 次 |
| 最近记录: |