有一个Jetpack Compose中的布局代码实验室,其中包含底层的布局修饰符步骤,解释了修改器顺序,请参见“顺序很重要”部分。
当链接修改器时,顺序很重要,因为它们从左到右应用于修改的可组合项,这意味着左侧的修改器的测量和布局将影响右侧的修改器。可组合项的最终大小取决于传递的所有修改器参数。首先,修改器会从左到右更新约束,然后,它们会从右到左返回大小。
为了更好地理解,我建议先了解Compose中的布局是如何工作的。简而言之,padding()是一个LayoutModifer,它接收一些约束条件,根据该约束条件的投影测量其子大小,并将子放置在某些坐标上。
让我们看一个例子:
Box(
modifier = Modifier
.border(1.dp, Color.Red)
.size(32.dp)
.padding(8.dp)
.border(1.dp, Color.Blue)
)
结果如下:
但是让我们交换.size()
和.padding()
Box(
modifier = Modifier
.border(1.dp, Color.Red)
.padding(8.dp)
.size(32.dp)
.border(1.dp, Color.Blue)
)
现在我们有了不同的结果:
我希望这个示例能帮助你理解修饰符是如何应用的。@Composable
fun MyFancyButton(modifier: Modifier = Modifier) {
Text(
text = "Ok",
modifier = modifier
.clickable(onClick = { /*do something*/ })
.background(Color.Blue, RoundedCornerShape(4.dp))
.padding(8.dp)
)
}
通过将modifier
移动到参数中,可组合项允许其父级添加其他修饰符,例如额外的边距。因为最后添加的修饰符最接近按钮、边框和内部填充,所以不会影响它们。
border()
来创建多个边框,这很有趣。 - ericn
package com.example.myapplication
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.setContent
import androidx.compose.ui.unit.dp
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("Hi there!",
Modifier
.border(2.dp, Color.Green)
.padding(50.dp)
.border(2.dp, Color.Red)
)
}
}
}
修改器(Modifier)允许我们自定义组合的外观。 使用它,您可以:
根据您放置这些修改器的顺序,您的组合的视觉和行为结构会被塑造。
大多数的修改器从上到下或从左到右应用。 Modifier.pointerInput()是一个例外,它默认情况下从右往左或从下往上应用。
Jetpack Compose
中的 Modifier.padding()
根据顺序作为填充或边距。
Modifier.padding(10.dp).size(200.dp)
在设置大小之前添加空白,你将得到一个带有200.dp大小的组合。
Modifier.size(200.dp).padding(10.dp)
在设置 10.dp 的内边距后,添加了填充,使得你的组合宽度和高度都是180.dp。
Box(
Modifier
.border(2.dp, Color.Green)
.padding(10.dp)
.border(2.dp, Color.Red)
.size(200.dp)
)
Box(
Modifier
.border(2.dp, Color.Cyan)
.size(200.dp)
.padding(10.dp)
.border(2.dp, Color.Magenta)
)
而且填充修饰符是累加的。Modifier.padding(20.dp).padding(20.dp)
相当于40.dp。
Box(
Modifier
.border(2.dp, Color.Green)
.padding(20.dp)
.border(2.dp, Color.Red)
.size(200.dp)
)
Box(
Modifier
.border(2.dp, Color.Green)
.padding(20.dp)
.padding(20.dp)
.border(2.dp, Color.Red)
.size(200.dp)
)
另一个修改器可以根据应用的顺序改变 Composable 的外观。如果要将阴影应用于 Composable 的外部,则应在背景或其他修饰符之前应用它。如果您在 Modifier.background
之后应用它,则可以获得外部阴影。
Box(
Modifier
.shadow(5.dp, RoundedCornerShape(8.dp))
.background(Color.White)
.size(100.dp)
)
Box(
Modifier
.background(Color.White)
.size(100.dp)
.shadow(5.dp, RoundedCornerShape(8.dp))
)
此修饰符还根据其放置顺序剪裁其他修饰符。使用此修饰符的好处是,如果您将其放置在 Modifier.clickable{}
之前,可以更改或剪切可组合项的可点击区域。使用此修饰符和 Shape
,可以创建圆形、三角形或钻石圆形区域或在布局前/后创建这些图形。
它是底层的 Modifier.graphicsLayer{}
,您可以查看我关于 此处, 此处 和 此处 的详细答案。它可以帮助您使用比例、形状、剪切、平移和其他很酷的属性创建复杂的布局。
这个修饰符用于在布局后更改 Composable 的位置,与 Modifier.padding 改变此修饰符的值不会改变 Composable 相对于其兄弟 Composable 的位置不同。但是,根据您设置 Modifier.offset 的位置,可以更改 Composable 的触摸区域,并且它有两个变体。一个采用 lambda defers state read,建议使用谷歌的这个,而不是采用值。
我使用了带有值的示例。您可以看到,如果首先应用偏移量,则随着滑块更改值,跟随偏移量的第一个修饰符将移动。在第二个示例中,由于在 Modifier.offset{}
之前应用了 Modifier.clickable{}
,因此 Composable 的触摸区域没有更改
var offset by remember {
mutableStateOf(0f)
}
Box(
Modifier
.offset(x = offset.dp)
.clickable {}
.background(Color.Red)
.size(100.dp)
)
Box(
Modifier
.clickable {}
.offset(x = offset.dp)
.background(Color.Red)
.size(100.dp)
)
Slider(value = offset, onValueChange = { offset = it }, valueRange = 0f..200f)
这个修饰符是手势和触摸事件的基础。使用它可以调用拖动、轻击、按压、双击、缩放、旋转和许多手势。在这个答案中,解释了如何使用它来构建View系统的onTouchEvent对应项。
与上面的修饰符不同,它默认从底部向上传播,除非你消耗PointerInputChange。在Compose手势系统中,消耗连续事件会取消下一个接收它的事件。因此,当你缩放图像时,可以防止出现滚动等手势。
Modifier
.pointerInput() // Second one that is invoked
.pointerInput() // First one that is invoked
首个padding就像该元素的margin一样。
比较这些组合,您将看到差异。
@Composable
fun Example() {
// Default
Box(modifier = Modifier.background(Color.Cyan), alignment = Alignment.Center){
Text("Hi there!", Modifier.border(2.dp, Color.Magenta))
}
Divider()
// 10dp margin
Box(modifier = Modifier.background(Color.Cyan), alignment = Alignment.Center){
Text("Hi there!", Modifier.padding(10.dp).border(2.dp, Color.Magenta))
}
Divider()
// 10dp margin and 10dp padding
Box(modifier = Modifier.background(Color.Cyan), alignment = Alignment.Center){
Text("Hi there!", Modifier.padding(10.dp).border(2.dp, Color.Magenta).padding(10.dp))
}
}
@Composable
fun test() {
Text("Hi there!",
Modifier.background(color = Color.Green)
.padding(10.dp)
.border(2.dp, Color.Magenta)
.padding(30.dp)
.border(2.dp, Color.Red)
.padding(80.dp)
)
}