Jetpack Compose:约束 和 修饰符 顺序

1,081 阅读8分钟

原文链接

MAD 技能:Compose 布局 和 修饰符 第 3 集

1_Tdb12lgfPZ1DC66ar-mY_g.webp

上一篇 MAD 技能文章 中,您了解了将数据转换为 UI 的 Compose 的三个阶段。我们创建了一个 思维模型 来帮助我们推理 app 的设计实现。在这一集中,我们将使用该思维模型来学习推理 修饰符链 以及它是如何影响我们的可组合项的大小的。

您也可以将本文作为 MAD 技能视频观看: youtu.be/OeC5jMV342A

还记得上一集我们有 三个阶段 将数据转换为 UI 吗:

  1. 组合:  显示什么
  2. 布局: 放在哪儿
  3. 绘制:  如何渲染

修饰符可以影响不同的阶段。例如,sizepadding 修饰符在布局阶段影响可组合项的大小和间距,而 clip 修饰符在绘制阶段影响可组合项的形状:

1_tAQ4mOKqZoseI7c_fBul9Q.webp

我们还知道,我们可以向可组合项添加多个修饰符,从而 创建一个链条。然而,这些链中的不同修饰符是如何相互影响的,却并不总是显而易见。

自己试试

让我们从一些练习开始。对以下每个代码片段,请尝试找出 哪个选项将是执行该片段的结果,选项 A 或 B:

问题 1

/* Copyright 2023 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 */

Image(
  painterResource(R.drawable.frag), 
  contentDescription = null,
  Modifier
    .fillMaxSize()
    .size(50.dp)
)

1_f2nKnYBAqaBq6Pgueb3KyQ.webp

问题 2

/* Copyright 2023 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 */

Image(
  painterResource(R.drawable.frag), 
  contentDescription = null,
  Modifier
    .fillMaxSize()
    .wrapContentSize()
    .size(50.dp)
)

1_D-ZoCNPsE0RDt38-nlwhqg.webp

问题 3

/* Copyright 2023 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 */

Image(
  painterResource(R.drawable.frag), 
  contentDescription = null,
  Modifier
    .clip(CircleShape)
    .padding(10.dp)
    .size(100.dp)
)

1_kUJ2etewUSNr0qOZV_e0cw.webp

不能确定答案?那你来对地方了!继续阅读来了解更多信息。(往这儿看,答案就在博文末尾)

约束

要了解如何推理修饰符顺序,我们必须了解布局阶段 Constraints 的作用。

还记得吗,在上一篇文章中我们讨论了布局阶段如何遵循 三步算法 找到每个布局节点的宽度、高度和 x、y 坐标:

  1. 测量子节点: 一个节点测量它的子节点,如果有的话。
  2. 决定自身大小: 基于这些测量,节点将决定自身大小。
  3. 放置子节点: 每个子节点都相对于节点自身的位置放置。

在此算法的前两个步骤中,约束有助于 为我们的节点找到合适的大小。它们是节点宽度和高度的 最小和最大边界。当节点决定它的大小时,它的测量大小应该落在这个给定的大小范围内。

在算法的第一步中,约束在 UI 树中 从父级传递给子级。当父节点测量其子节点时,它会向每个子节点提供这些约束,让他们知道允许的大小。然后,当它决定自己的大小时,它也会遵守由自己的父级们传入的约束。

约束的类型

约束可以是 有界的,表示最小和最大的宽度和高度:

1_wJshWGx1WJhmGnQzCWc1YQ.webp

有界的约束

约束也可以是 无界的,在这种情况下节点不受任何大小的约束。 然后将最大的宽度和高度边界设置为无穷大:

1_nG0aK3sJDrkbFg8rSTW6dA.webp

无界的约束

又或者约束也可以是 精确的,要求节点遵循精确的大小要求。最小和最大的边界设置为相同的值:

1_1z2WYrM6DZhk-g1umnit4Q.webp

精确的约束

当然,这些的组合也是有效的,例如限制宽度,同时允许无限制的最大高度,或者设置精确宽度但提供有界高度:

1__-PgKRMzpdwJXg6dzIPTtg.webp

有界的、无界的 和 精确的 宽度和高度的组合

演练算法

要了解约束如何从父级传递给子级,以及如何根据这些约束解析大小,最好 看一个例子。不管怎样,这在视频格式中都更容易呈现,所以我建议您 观看 MAD 技能视频的“示例”一章

youtu.be/OeC5jMV342A

修饰符及其对约束的影响

通过观看视频,您应该很好地理解 约束如何影响可组合项的大小,以及 修饰符如何影响这些约束。 在本节中,我们将仔细研究一些特定的修饰符以及它们是如何影响约束的。

size 修饰符

让我们看看下面的 UI 树,它应该在 300dp 乘 200dp 的容器中呈现。约束是 有界的,允许宽度在 100dp300dp 之间,高度在 100dp200dp 之间。

1_8DByym_lCLvq-xUtlkZHdA.webp

size 修饰符 调整传入的约束以匹配传递给它的值,例如 150dp

1_xZ7fnIR_13E-7w-gg8TAoQ.webp

但是,如果 请求的尺寸太小或太大 怎么办?也就是说,如果宽度和高度小于最小约束边界,或者大于最大约束边界怎么办?

1_kGb0chioAFuk7vJvMN522A.webp

在这种情况下,修饰符将尝试 尽可能接近地匹配传递的约束,同时仍然遵守传入的约束:

1_NP-IvX9hON51vZw9qqX5uQ.webp

这也解释了为什么 链接多个size修饰符不起作用。 第一个 size 修饰符会将最小和最大约束设置为固定值,即使第二个 size 修饰符请求更小或更大的大小,它仍然需要遵守传入的确切边界,因此它不会覆盖这些值:

1_a0R5jT2twQuIRlDV3hfKnw.webp

requiredSize 修饰符

如果您确实需要您的节点 覆盖传入的约束,您可以将 size 修饰符替换为另一个名为 requiredSize 的修饰符。 它将替换传入的约束并传递您指定的大小,作为精确边界。 然后,当大小被传递回树时,子节点将在可用空间中居中:

1_JO78Zw2On2kuEQvxGJOFdQ.webp

width 和 height 修饰符

在前面的示例中,我们使用了 size 修饰符,它可以适应约束的宽度和高度。但是,我们也可以将它们替换为 width 修饰符,设置一个 固定的宽度,但保留未定的高度。或者我们可以使用 height 修饰符,它设置了一个 固定的高度,但保留未定的宽度

1_iKdD3wSrfzjvLCSxCQShrw.webp

sizeIn 修饰符

如果您需要对约束进行细粒度控制,并希望 根据您的具体需求调整它们,您可以使用 sizeIn 修饰符:

1__0pv-q8GokAjiMbYcLDnmQ.webp

练习的答案

既然我们了解了约束以及它们如何影响测量,那让我们回到我们最初的用例并找到正确的解决方案。

问题 1

/* Copyright 2023 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 */

Image(
  painterResource(R.drawable.frag), 
  contentDescription = null,
  Modifier
    .fillMaxSize()
    .size(50.dp)
)

这是解决方案:

1_QcuUtHc9CxV_0N_sKbp24Q.webp

  • fillMaxSize 修饰符更改约束以将最小宽度和高度都设置为最大值 - 宽度为 300dp,高度为 200dp
  • 因此,即使 size 修饰符想要使用 50dp 的大小,它仍然需要遵守传入的最小约束。因此,大小修饰符还将输出 300 乘以 200 的精确约束边界,有效地 忽略 size 修饰符中提供的
  • Image 遵循这些边界并(向上)报告大小为 300 乘以 200,这个大小将一直向上传递。

问题 2

/* Copyright 2023 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 */

Image(
  painterResource(R.drawable.frag), 
  contentDescription = null,
  Modifier
    .fillMaxSize()
    .wrapContentSize()
    .size(50.dp)
)

这是解决方案:

1_cPPwWUT9E1Fs4vhF4SSyrw.webp

  • fillMaxSize 修饰符的行为仍然相同,并调整约束以将最小宽度和高度都设置为最大值 - 宽度为 300dp,高度为 200dp
  • wrapContentSize 修饰符重置最小约束。 因此,虽然 fillMaxSize 导致固定约束,但 wrapContentSize 将其重置回有界约束。下面的节点现在可以再次占据整个空间,或者小于整个空间。
  • size 修饰符将约束设置为 50 的最小和最大界限。
  • Image 解析为 5050 的大小,并且 size 修饰符将其转发。
  • wrapContentSize 修饰符有一个特殊的属性。它接受它的子项,并 将它放在 传递给它的 可用最小边界的中心。因此,它与父级通信的大小等于传递给它的最小界限。

通过组合仅仅三个修饰符,我们就能够为我们的可组合项定义一个尺寸,并将其居中于其父级!

问题 3

/* Copyright 2023 Google LLC. 
   SPDX-License-Identifier: Apache-2.0 */

Image(
  painterResource(R.drawable.frag), 
  contentDescription = null,
  Modifier
    .clip(CircleShape)
    .padding(10.dp)
    .size(100.dp)
)

这是解决方案:

1_IU9X8ChIi_H276yDMDbW6Q.webp

  • clip 修饰符不会更改约束。
  • padding 修饰符降低了最大约束。
  • size 修饰符将所有约束设置为 100dp
  • Image 遵守这些约束并报告大小为 100 乘以 100dp
  • padding 修饰符在所有尺寸上添加 10dp,因此它会将报告的宽度和高度增加 20dp
  • 现在,在绘制阶段,clip 修饰符作用于 120120dp 的画布。因此它 创建了一个该大小的圆形蒙版。
  • padding 修饰符然后在所有尺寸上以 10dp 插入其内容,因此它把画布尺寸降低到了 100 乘 100dp
  • 然后在该画布中绘制 Image。你可以看到图像是根据原来的 120dp 圆进行裁剪的,因此我们看到了非圆的结果。

总结

哇,今天内容真不少!您了解了 约束,并使用它们来推理修饰符、如何对其进行排序以及测量。

在下一篇文章中,我们将向您展示如何使用这些信息来开始实现 您自己的自定义布局

这篇博文是系列博文的一部分:

第 1 集:Compose 布局 和 修饰符 的基础知识
第 2 集:Compose 的各个阶段
第 3 集:约束 和 修饰符 顺序
第 4 集:高级布局概念