Jetpack Compose - 修饰符顺序

Jetpack Compose - Order of Modifiers

文档说修改器是从左边应用的。 但是从这个例子来看,它们似乎是从右边应用的: 先border再padding因为text和border

之间没有space
Text("Hi there!", Modifier.padding(10.dp).border(2.dp, Color.Magenta))

在这种情况下,第一个填充类似于元素的边距。

比较这些可组合项,您会发现不同之处。

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

“可以使用 then 组合修饰符元素。顺序很重要;首先出现的修饰符元素将首先应用。” @here

它首先应用填充 10.dp 的外层,然后应用 color.Magenta 的边框,依此类推(“从左到右”)。 80.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)
    )
}

  • In Android Compose 正在从外层向中心的 Composable 构造生成的图像。 这意味着首先定义的 Green border 是 outer border ,最后定义的 Red border 是 inner border 。 这非常令人困惑,因为在代码中最接近 Text Composable 的 Green Modifier 在结果中离它最远。
  • 这与 SwiftUI 形成对比,SwiftUI 中修饰符在代码和结果图像中的出现顺序相同。 在代码中最接近可组合项的修饰符在生成的图像中也最接近它。
  • 如果您想想象生成的图像是从可组合项所在的中心构建的(如在 SwiftUI 中),那么修饰符的应用顺序与给定顺序相反(从下向上)。
  • 因此,如果您有带有两个边框修饰符的可组合文本
    • 代码中距文本可组合项最远的边界修饰符(底部的红色)
    • 将最接近结果图像中的可组合文本
  • 修饰符从外层向内层应用
    • 在最外层应用.border(2.dp, Color.Green)
    • 向内应用 .padding(50.dp)
    • 将.border(2.dp, Color.Red) 应用到最内层
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)
      )
    }
  }
}

Layouts in Jetpack Compose codelab containing Layout modifiers under the hood 个步骤解释修饰符顺序,请参阅 “顺序很重要” 部分。

order matters when chaining modifiers as they're applied to the composable they modify from earlier to later, meaning that the measurement and layout of the modifiers on the left will affect the modifier on the right. The final size of the composable depends on all modifiers passed as a parameter. First, modifiers will update the constraints from left to right, and then, they return back the size from right to left.

为了更好地理解它,我建议弄清楚 layouts work in Compose. In short, padding() is a LayoutModifer,它如何接受一些约束,根据约束的投影测量其 child 大小,并将 child在一些坐标。

我们来看一个例子:

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 移动到参数,可组合项就允许其 parents 添加额外的修饰符,例如额外的边距。因为最后添加的修饰符离按钮最近,所以边框和内边距不会受到影响。