终于可以写写技术文了~ 最近忙着各种总结,想必大家也是一样的吧?今年年初的规划,现在完成的怎么样了呢?是不是也像我一样“虎头蛇尾”?哈哈!至少竹子比去年进步了不少,这是今年的最后一篇啦!希望2022年大家一起加油!一起进步!
固有特性测量的基本用法
// code 1
@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
Row(modifier = modifier) {
Text(
modifier = Modifier
.weight(1f)
.wrapContentWidth(Alignment.CenterHorizontally),
text = text1
)
Divider(color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp))
Text(
modifier = Modifier
.weight(1f)
.wrapContentWidth(Alignment.CenterHorizontally),
text = text2
)
}
}
fillMaxHeight
,它会尽可能撑大父布局。那么要达到我们想要的效果,就需要使用固有特性 IntrinsicSize
先规定一下测量的方式,这里需要将 Row 的 height 设置为 IntrinsicSize.Min
,即把 Row 的高度调整为尽可能小的固有高度。具体代码如下:// code 2
@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
Row(modifier = modifier.height(IntrinsicSize.Min)) {
...
}
}
IntrinsicSize
预先获取到了它左右两边的 Text 组件的高度信息了,然后计算出了两个 Text 组件的高度最大值作为它自己的高度值,最后将分割线的高度铺满整个父组件。固有特性测量实现的关键点
height(IntrinsicSize.Min)
进入,到了 Intrinsic.kt 中的一个静态内部类中:// code 3
private object MinIntrinsicHeightModifier : IntrinsicSizeModifier {
override fun MeasureScope.calculateContentConstraints(
measurable: Measurable,
constraints: Constraints
): Constraints {
val height = measurable.minIntrinsicHeight(constraints.maxWidth)
return Constraints.fixedHeight(height)
}
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
) = measurable.minIntrinsicHeight(width)
}
IntrinsicSizeModifier
这个接口,而这个 IntrinsicSizeModifier
接口实际上又是实现了 LayoutModifier
接口:// code 4
private interface IntrinsicSizeModifier : LayoutModifier {
val enforceIncoming: Boolean get() = true
fun MeasureScope.calculateContentConstraints(
measurable: Measurable,
constraints: Constraints
): Constraints
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
val contentConstraints = calculateContentConstraints(measurable, constraints)
val placeable = measurable.measure(
if (enforceIncoming) constraints.constrain(contentConstraints) else contentConstraints
)
return layout(placeable.width, placeable.height) {
placeable.placeRelative(IntOffset.Zero)
}
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: Int
) = measurable.minIntrinsicWidth(height)
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
) = measurable.minIntrinsicHeight(width)
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: Int
) = measurable.maxIntrinsicWidth(height)
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: Int
) = measurable.maxIntrinsicHeight(width)
}
measurable.minIntrinsicHeight()
这一类的方法。比如在 code 3 中,重写的 MeasureScope.calculateContentConstraints
方法和 IntrinsicMeasureScope.maxIntrinsicHeight
方法,最重要的部分都是调用了 measurable.minIntrinsicHeight()
方法。这个 minIntrinsicHeight
都会传一个 width 参数进去,点进去,发现是一个 IntrinsicMeasurable
接口,接口方法的说明如下所示:// code 5 IntrinsicMeasurable.kt
/**
* Calculates the minimum height that the layout can be such that
* the content of the layout will be painted correctly.
*/
fun minIntrinsicHeight(width: Int): Int
IntrinsicMeasurable
接口的实现方式就不同了,但是没有找到 Row 组件具体实现的源码。。此路不通,看有没有其他的路,在上篇笔记三中,我们知道 Composable 组件计算自身宽高是在 Layout 方法中进行的,那么从 Layout 处入手看会怎样呢?// code 6 Layout.kt
@Composable inline fun Layout(
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
······
}
IntrinsicMeasureScope.minIntrinsicHeight()
方法:// code 7 MeasurePolicy.kt
/**
* The function used to calculate [IntrinsicMeasurable.minIntrinsicHeight]. It represents
* defines the minimum height this layout can take, given a specific width, such
* that the content of the layout will be painted correctly.
*/
fun IntrinsicMeasureScope.minIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int
): Int {
val mapped = measurables.fastMap {
DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Min, IntrinsicWidthHeight.Height)
}
val constraints = Constraints(maxWidth = width)
val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
val layoutResult = layoutReceiver.measure(mapped, constraints)
return layoutResult.height
}
IntrinsicMeasurable.minIntrinsicHeight
,它定义了在给定宽度的情况下,该布局在正确绘制布局内容的情况下,可以获得的最小高度。measurable.minIntrinsicHeight(constraints.maxWidth)
方法。。。呃。。又回到了之前的 IntrinsicMeasurable 接口中的 fun minIntrinsicHeight(width: Int): Int
方法(笑Cry.jpg)。虽然没有找到真正实现这个接口的代码,但是通过上面的源码跟踪,竹子也得知了两个关键点。minIntrinsicHeight
方法,同样的还有 maxIntrinsicHeight
、minIntrinsicWidth
、maxIntrinsicWidth
这一类的方法都是在 IntrinsicMeasurable 接口,真正实现了这四个方法的地方就是真正实现了固有特性测量的地方。// code 8 LayoutModifier.kt DefaultIntrinsicMeasurable class
override fun measure(constraints: Constraints): Placeable {
if (widthHeight == IntrinsicWidthHeight.Width) {
val width = if (minMax == IntrinsicMinMax.Max) {
measurable.maxIntrinsicWidth(constraints.maxHeight)
} else {
measurable.minIntrinsicWidth(constraints.maxHeight)
}
return EmptyPlaceable(width, constraints.maxHeight)
}
val height = if (minMax == IntrinsicMinMax.Max) {
measurable.maxIntrinsicHeight(constraints.maxWidth)
} else {
measurable.minIntrinsicHeight(constraints.maxWidth)
}
return EmptyPlaceable(constraints.maxWidth, height)
}
widthHeight = IntrinsicWidthHeight.Height
,minMax = IntrinsicMinMax.Min
,所以就是调用的 measurable.minIntrinsicHeight(constraints.maxWidth)
,也就是将 约束条件中 width 最大值传给了方法。Modifier.height(IntrinsicSize.Min)
为 Row 的高度设置了固有特性测量并没有设置宽度啊?那是因为会将约束条件的 width 最大值作为默认值传进去,如 code 3 中的代码,这里的最大值其实就是不限制宽度的大小,所以 Modifier.height(IntrinsicSize.Min)
所表达的意思就是,当宽度不限时通过子组件预先测量的宽高信息所能计算的 Row 最小高度是多少。当然也可以自己设置一个宽度,那么子组件就可以根据你设置的 Row 宽度以及预先测量的宽高信息得出 Row 的最小高度是多少。这就是关键点二。实现自定义layout中的固有特性测量
Layout()
方法,如果我们要适配自己写的自定义 Layout 的固有特性测量,就需要对 Layout()
方法中的 MeasurePolicy 接口进行重写了。measure
方法,如果要实现固有特性测量,则还需要重写相应的 Intrinsic 方法,具体来说一共有四个:override fun IntrinsicMeasureScope.minIntrinsicHeight(measurables: List<IntrinsicMeasurable>, width: Int)
override fun IntrinsicMeasureScope.maxIntrinsicHeight(measurables: List<IntrinsicMeasurable>, width: Int)
override fun IntrinsicMeasureScope.minIntrinsicWidth(measurables: List<IntrinsicMeasurable>, height: Int)
override fun IntrinsicMeasureScope.maxIntrinsicWidth(measurables: List<IntrinsicMeasurable>, height: Int)
minIntrinsicWidth
方法:// code 9
// 自定义 Column 组件
@Composable
fun MyColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content,
measurePolicy = object: MeasurePolicy {
override fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult {
// 自定义 Layout 实现测量与摆放的逻辑
var height = 0 // 自定义 Layout 高度
var width = 0 // 自定义 Layout 宽度
val placeables = measurables.map {
val placeable = it.measure(constraints)
height += placeable.height
width = max(width, placeable.width)
placeable
}
return layout(width, height) {
var lastHeight = 0
placeables.map {
it.placeRelative(0, lastHeight)
lastHeight += it.height
}
}
}
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int
): Int {
TODO("Not yet implemented")
}
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurables: List<IntrinsicMeasurable>,
width: Int
): Int {
TODO("Not yet implemented")
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurables: List<IntrinsicMeasurable>,
height: Int
): Int {
var maxWidth = 0
measurables.forEach {
maxWidth = max(maxWidth, it.minIntrinsicWidth(height))
}
return maxWidth
}
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurables: List<IntrinsicMeasurable>,
height: Int
): Int {
TODO("Not yet implemented")
}
})
}
// code 10
MyColumn(modifier = Modifier.width(IntrinsicSize.Min)) {
Text(text = "watermelon")
Text("apple")
Divider(color = Color.Black, modifier = Modifier.height(2.dp).fillMaxWidth())
Text("orange")
}
Modifier.width(IntrinsicSize.Min)
固有特性测量,则显示的效果就会是这样的:minIntrinsicWidth
和 measure
方法中分别打上断点,可以发现,Compose 确实是在 measure 父组件前,就先调用了 minIntrinsicWidth
方法去获取了子组件的宽高。总结
更多内容,欢迎关注我的公众号留言交流~
参考文献
-
https://developer.android.google.cn/codelabs/jetpack-compose-layouts?continue=https%3A%2F%2Fdeveloper.android.google.cn%2Fcourses%2Fpathways%2Fcompose%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fjetpack-compose-layouts#10
原文始发于微信公众号(修之竹):Jetpack-Compose 学习笔记(四)—— Intrinsic 固有特性测量是个啥?看完这篇就知道了
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/100567.html