首页 >> 大全

聊一聊 Compose 的固有特性测量 Intrinsic | 开发者说·DTa

2023-11-12 大全 31 作者:考证青年

目前有一个正在进行的 中文手册项目,旨在帮助开发者更好的理解和掌握 框架,目前仍还在开荒中,欢迎大家进行关注与加入!

什么是固有特性测量

在自定义 中我们提到 布局原理, 中的每个 UI 组件是不允许多次进行测量的,多次测量在运行时会抛异常,禁止多次测量的好处是为了提高性能,但在很多场景中多次测量子 UI 组件是有意义的。在 代码实验室中就提供了这样一种场景,我们希望中间分割线高度与两边文案高的一边保持相等。

为实现这个需求,官方所提供的设计方案是希望父组件可以预先获取到两边的文案组件高度信息,然后计算两边高度的最大值即可确定当前父组件的高度值,此时仅需将分割线高度值铺满整个父组件即可。

为了实现父组件预先获取文案组件高度信息从而确定自身的高度信息, 为开发者们提供了固有特性测量机制,允许开发者在每个子组件正式测量前能获取到每个子组件的宽高等信息。

在基础组件中使用固有特性测量

使用固有特性测量的前提是当前作用的 需要适配固有特性测量,目前许多基础组件已经完成对固有特性测量的适配,可以直接使用。

在上面所提到的例子中父组件所提供的能力使用基础组件中的 Row 组件即可承担,我们仅需为 Row 组件高度设置固有特性测量即可。我们使用 .(.Min)即可为高度设置固有特性测量。

@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {Row(modifier = modifier.height(IntrinsicSize.Min)) { // I'm hereText(modifier = Modifier.weight(1f).padding(start = 4.dp).wrapContentWidth(Alignment.Start),text = text1)Divider(color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp))Text(modifier = Modifier.weight(1f).padding(end = 4.dp).wrapContentWidth(Alignment.End),text = text2)}
}@Preview
@Composable
fun TwoTextsPreview() {LayoutsCodelabTheme {Surface {TwoTexts(text1 = "Hi", text2 = "there")}}
}

通过使用固有特性测量即可完成上面所述场景的需求,展示效果如图所示。

值得注意的是此时我们的 仅使用 .(.Min)为高度设置了固有特性测量,并没有进行宽度的设置。此时所表达的意思是,当宽度不限时通过子组件预先测量的宽高信息所能计算的高度最少可以是多少。当然您也可以进行宽度的设置,当宽度受限时通过子组件预先测量的宽高信息所能计算的高度最少可以是多少。

可能您不能理解宽度受限可能影响高度这件事,其实我们常用的 Text 组件当宽度收到不同限制时,其高度就是不一样的。

Column(Modifier.fillMaxSize()) {Box(Modifier.width(50.dp).background(Color.Red)) {Text(text = "Jetpack Compose is an excellent development tool")}Box(Modifier.width(100.dp).background(Color.Yellow)) {Text(text = "Jetpack Compose is an excellent development tool")}
}

伏安特性测量实验报告__什么叫固有机械特性

展示效果如下。

⚠️ 注意事项:您只能对已经适配固有特性测量能力的组件使用 .Min 或 .Max,否则程序会运行时抛出异常而崩溃。对于所有自定义 的开发者来说如果支持使用者使用固有特性测量,则必须要进行固有特性测量的适配工作。

为自定义 适配固有特性测量

从上面的例子可以发现,我们仅使用.(.Min)即可交给 Row 组件根据子组件的信息进行计算从而确定一个固定的高度。然而之前他时如何操作的,对于开发者而言是完全未知的。所以本文将继续深入下去,通过一个自定义 适配固有特性测量的过程来摸清固有特性测量的整个流程。

重写 固有特性测量相关方法

对于适配固有特性测量的 ,我们需要对 下的固有特性测量方法进行重写。还记得 是谁嘛?没错他就是我们在自定义 中传入的最后的 SAM 转换的实例类型。

@Composable inline fun Layout(content: @Composable () -> Unit,modifier: Modifier = Modifier,measurePolicy: MeasurePolicy
)  

对于固有特性测量的适配,我们需要根据需求重写以下四个方法。

.width(.Max) 对应

.width(.Min) 对应

.(.Max) 对应

.(.Min) 对应

⚠️ 注意事项:如果哪个 使用了, 但其对应方法没有重写仍会崩溃。在 声明时,我们就不能使用 SAM 形式了,而是要规规矩矩实现 。

@Composable
fun IntrinsicRow(modifier: Modifier, content: @Composable () -> Unit){Layout(content = content,modifier = modifier,measurePolicy = object: MeasurePolicy {override fun MeasureScope.measure(measurables: List,constraints: Constraints): MeasureResult {TODO("Not yet implemented")}override fun IntrinsicMeasureScope.minIntrinsicHeight(measurables: List,width: Int): Int {TODO("Not yet implemented")}override fun IntrinsicMeasureScope.maxIntrinsicHeight(measurables: List,width: Int): Int {TODO("Not yet implemented")}override fun IntrinsicMeasureScope.maxIntrinsicWidth(measurables: List,height: Int): Int {TODO("Not yet implemented")}override fun IntrinsicMeasureScope.minIntrinsicWidth(measurables: List,height: Int): Int {TODO("Not yet implemented")}}) 
}

在我们的案例中仅使用了 .(.Min),出于简单考虑仅重写了 以作示例。

与 有相同的两个参数 与 width:

Modifier.widthIn(100.dp, 200.dp) //在此场景下minIntrinsicHeight的参数width值为200.dp对应的px.height(IntrinsicSize.Max)

接下来我们使用 即可获取到每个子组件在给定宽度下能够保证正确展示的最小高度,这个正确展示的高度是由子组件来保证的。再得到所有子组件的高度信息后,我们即可计算最大高度值,此值将会被设置为当前父组件 (也就是当前自定义 ) 的固定高度。

override fun IntrinsicMeasureScope.minIntrinsicHeight(measurables: List,width: Int
): Int {var maxHeight = 0measurables.forEach {maxHeight = it.minIntrinsicHeight(width).coerceAtLeast(maxHeight)}return maxHeight
}

在 中适配

接下来我们将所有使用的 声明出来。

IntrinsicRow(modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Min)
) {Text(text = "Left", Modifier.wrapContentWidth(Alignment.Start).layoutId("main"))Divider(color = Color.Black,modifier = Modifier.width(4.dp).fillMaxHeight().layoutId("devider"))Text(text = "Right", Modifier.wrapContentWidth(Alignment.End).layoutId("main"))
}

此时,由于声明了 .(),导致我们自定义 宽度是固定的,又因为我们使用了固有特性测量,此时我们自定义 的高度也是固定的。具体表现为 参数中 与 相等 (宽度固定), 与 相等 (高度固定)。

而我们希望 测量的宽度不应是固定与父组件相同,而是要根据其自身声明的宽度,也就是 .width(4.dp),所以我们对 测量使用的 进行了修改。将其最小值设置为零。

@Composable
fun IntrinsicRow(modifier: Modifier, content: @Composable () -> Unit){Layout(content = content,modifier = modifier,measurePolicy = object: MeasurePolicy {override fun MeasureScope.measure(measurables: List,constraints: Constraints): MeasureResult {var devideConstraints = constraints.copy(minWidth = 0)var mainPlaceables = measurables.filter {it.layoutId == "main"}.map {it.measure(constraints)}var devidePlaceable = measurables.first { it.layoutId == "devider"}.measure(devideConstraints)var midPos = constraints.maxWidth / 2return layout(constraints.maxWidth, constraints.maxHeight) {mainPlaceables.forEach {it.placeRelative(0, 0)}devidePlaceable.placeRelative(midPos, 0)}}override fun IntrinsicMeasureScope.minIntrinsicHeight(measurables: List,width: Int): Int {var maxHeight = 0measurables.forEach {maxHeight = it.maxIntrinsicHeight(width).coerceAtLeast(maxHeight)}return maxHeight}})
}

最终效果如下,这样我们就为我们自定义 适配了固有特性测量能力。

对固有特性测量的思考

固有特性测量的本质就是父组件可在正式测量布局前预先获取到每个子组件宽高信息后通过计算来确定自身的固定宽度或高度,从而间接影响到其中包含的部分子组件布局信息。也就是说子组件可以根据自身宽高信息从而确定父组件的宽度或高度,从而影响其他子组件布局。在我们使用的方案中,我们通过文案子组件的高度确定了父组件的固定高度,从而间接确定了分割线的高度。此时子组件要通过固有特性测量这种方式,通过父组件而对其他子组件产生影响,然而在有些场景下我们不希望父组件参与其中,而希望子组件间通过测量的先后顺序直接相互影响, 为我们提供了 来处理这类子组件存在依赖关系的场景。

长按右侧二维码

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了