Jetpack-Compose 学习笔记(四)—— Intrinsic 固有特性测量是个啥?看完这篇就知道了(上)

简介: Jetpack-Compose 学习笔记(四)—— Intrinsic 固有特性测量是个啥?看完这篇就知道了(上)

终于可以写写技术文了~ 最近忙着各种总结,想必大家也是一样的吧?今年年初的规划,现在完成的怎么样了呢?是不是也像我一样“虎头蛇尾”?哈哈!至少竹子比去年进步了不少,这是今年的最后一篇啦!希望2022年大家一起加油!一起进步!

这一篇是为了填上一篇学习笔记三中提到的 Compose 也可多次测量的“坑”,那就是固有特性测量。

Google 起的这名字个人感觉太不直观了,第一次看到这个官方的翻译真的让我一头雾水,这是个啥?其实,这个东西主要作用就是,调节需要展示的 Composable 组件的宽高大小。


固有特性测量的基本用法


前面文章中也提到了,Compose 有一项规则,即子元素只能测量一次,测量两次就会引发运行时异常。但是,有时又需要先收集一些关于子组件的信息,然后再测量父组件。那么,借助固有特性,就可以先查询子组件,然后再进行实际测量。下面是一个栗子。

假如需要像下面展示的那样:


根据之前讲的布局内容,我们很容易就可以写出如下代码:

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

text1 和 text2 设置为 “Hello”、“World”。实际展示居然是这样的:


嗯??怎么中间的分割线“放飞自我”了?这是因为 Row 没有对它的子组件的测量做任何限制,而 Divider 的高度设置的是 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)) {
        ...
    }
}

具体是如何做到的呢?实际上,是因为 Row 父组件通过 IntrinsicSize预先获取到了它左右两边的 Text 组件的高度信息了,然后计算出了两个 Text 组件的高度最大值作为它自己的高度值,最后将分割线的高度铺满整个父组件。

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

那么,这玩意儿是怎么实现的呢?

很遗憾竹子没有翻到源码,哪位大神如果找到的话,欢迎一起交流~

虽然没有找到源码,但是也知道了一些关键点。下面是找源码未遂的过程,不感兴趣的同学可以跳过。。


固有特性测量实现的关键点


从使用的地方开始,从 code 2 的 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)
}

综合 code 3 和 code 4 可以看出,核心的方法就是 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

方法的注释写的很清楚:计算能正确绘制 layout 内容时的 layout 的最小高度。OK,因为每个 Composable 组件摆放子组件的方式不同,所以每个组件的 IntrinsicMeasurable接口的实现方式就不同了,但是没有找到 Row 组件具体实现的源码。。此路不通,看有没有其他的路,在上篇笔记三中,我们知道 Composable 组件计算自身宽高是在 Layout 方法中进行的,那么从 Layout 处入手看会怎样呢?

从 Row 的 Layout 方法进入到 Layout.kt,测量的部分肯定是在 MeasurePolicy 类中:

// code 6    Layout.kt
@Composable inline fun Layout(
    content: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    measurePolicy: MeasurePolicy
) {
   ······
}


目录
相关文章
|
7月前
|
Android开发
Jetpack-Compose 学习笔记(四)—— Intrinsic 固有特性测量是个啥?看完这篇就知道了(下)
Jetpack-Compose 学习笔记(四)—— Intrinsic 固有特性测量是个啥?看完这篇就知道了(下)
49 0
|
4月前
|
Android开发 开发者
什么是Android Jetpack,它包括哪些组件?
什么是Android Jetpack,它包括哪些组件?
42 0
|
4月前
|
IDE API 开发工具
Google I/O :Android Jetpack 最新变化(四)Compose
Google I/O :Android Jetpack 最新变化(四)Compose
112 0
|
4月前
|
JSON IDE 测试技术
Google I/O :Android Jetpack 最新变化(二) Performance
Google I/O :Android Jetpack 最新变化(二) Performance
117 0
|
4月前
|
SQL API Android开发
Google I/O :Android Jetpack 最新变化(一) Architecture
Google I/O :Android Jetpack 最新变化(一) Architecture
75 0
|
4月前
|
API Android开发
Google I/O :Android Jetpack 最新变化(三)UI
Google I/O :Android Jetpack 最新变化(三)UI
53 0
|
2月前
|
XML API Android开发
【Android 从入门到出门】第三章:使用Hilt处理Jetpack Compose UI状态
【Android 从入门到出门】第三章:使用Hilt处理Jetpack Compose UI状态
33 4
|
8月前
|
程序员 开发工具 Android开发
Android JetPack App Startup 使用及源码浅析(二)
Android JetPack App Startup 使用及源码浅析
|
8月前
|
开发工具 Android开发
Android JetPack App Startup 使用及源码浅析(一)
Android JetPack App Startup 使用及源码浅析
|
9月前
|
Android开发
Android JetPack组件之ViewModel状态的保存(程序在后台被系统杀死数据也存活)
Android JetPack组件之ViewModel状态的保存(程序在后台被系统杀死数据也存活)
103 0