【问题标题】:Jetpack Compose - Order of ModifiersJetpack Compose - 修饰符的顺序
【发布时间】:2021-01-20 04:51:53
【问题描述】:

文档说修饰符是从左侧应用的。 但从这个例子来看,它们似乎是从右边应用的: 第一个边框,然后填充,因为文本和边框之间没有空格

Text("Hi there!", Modifier.padding(10.dp).border(2.dp, Color.Magenta))

【问题讨论】:

    标签: android android-jetpack android-jetpack-compose modifier


    【解决方案1】:

    在这种情况下,第一个填充就像元素的边距。

    比较这些 Composables,你会发现不同。

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

    【讨论】:

      【解决方案2】:

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

      它首先应用于外层,内边距为 10.dp,然后是边框,颜色为.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)
          )
      }
      

      【讨论】:

      • 你说“它首先适用于外层”。这不是倒退的想法吗?从文本本身(内层)开始,看看它是如何用不同的修饰符向外修改的,不是更合乎逻辑吗?因此,您的思维方式是向外向内应用定义的修饰符。您可以通过从内向外反向应用修饰符来获得相同的结果(正如我在帖子中所述)。换句话说,对我来说,从 Text(内层)开始更合乎逻辑,但在这种情况下,我需要以相反的顺序应用修饰符来预测结果
      • .padding(10.dp) 最接近代码中的文本,但距离图像最远。这是不合逻辑的。 .padding(80.dp) 离代码中的文本最远,但它最接近图像中的文本。这是不合逻辑的。但据此我得出结论,修饰符的实际应用顺序是相反的:代码中最靠前的会先应用。
      • 如果您使用“它首先应用于外层”的方法,这意味着当您首先阅读代码时,您会看到文本,然后您跳到远离文本(外层)的某个地方并返回到文本通过从外层到内层应用修饰符。这对我来说没有意义。对我来说,我们以与阅读代码相同的方式构建图像更合乎逻辑。首先有一些文本。然后它旁边是代码中的第一个修饰符,它也应该在图像中。因此,我们以与阅读代码相同的顺序构建图像。从内部向外,因为代码以内部的 Text 开头
      • 所以当我说修饰符应用在相反的方向时,我的意思是最接近代码中文本的修饰符在生成的图像中更远图像与代码的方向相反- 因此修改器以相反的方向应用。在代码中,洋红色边框是在红色边框之前定义的,它更接近文本。但在生成的图像中却相反:红色边框更接近文本,尽管它是在洋红色边框之后定义的。
      • SwiftUI 中的等效代码会产生相反的结果。在代码和图像中,洋红色边框会更接近文本。因为每个修改器都会返回新的修改视图。所以修饰符确实是按照它们定义的方向使用的。您从文本视图开始。您应用 Magenta 修改器,该修改器返回新的 View,在 Text 周围带有洋红色边框。然后对该视图应用填充和红色边框,使红色边框变为外部。一切都是合乎逻辑的,并且在代码和生成的图像中都以相同的自然方向流动。
      【解决方案3】:
      • 在 Android Compose 中,生成的图像是从外部层向中心的 Composable 构建的。 这意味着第一个定义的绿色边框是外边框,最后一个定义的红色边框是内边框。 这非常令人困惑,因为代码中最接近文本可组合的绿色修饰符在结果中离它最远。
      • 这与 SwiftUI 形成鲜明对比,SwiftUI 中的修饰符在代码和生成的图像中都以相同的顺序出现。 在代码中最接近 Composable 的修饰符在生成的 Image 中也最接近它。
      • 如果您想想象生成的 Image 是从您的 Composable 所在的中心构建的(就像在 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)
            )
          }
        }
      }
      

      【讨论】:

        【解决方案4】:

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

        在链接修饰符时,顺序很重要,因为它们被应用到它们从早到晚修改的组合,这意味着左侧修饰符的测量和布局将影响右侧的修饰符。可组合的最终大小取决于作为参数传递的所有修饰符。首先,修饰符将从左到右更新约束,然后,它们从右到左返回大小

        为了更好地理解它,我建议弄清楚 layouts 在 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 移动到参数,可组合项就允许其父项添加额外的修饰符,例如额外的边距。因为最后添加的修饰符离按钮最近,所以边框和内边距不会受到影响。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-02
          • 1970-01-01
          • 2021-10-13
          • 1970-01-01
          • 2021-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多