Modifier.layout

看下面这段代码

Column(Modifier.fillMaxWidth()) {
    Text(text = "hello world", Modifier.layout { measurable, constraints ->
        // 能够了解为 传统view 中丈量自己巨细的步骤 获得自己宽高的尺度
        val placeable = measurable.measure(constraints)
        // 拿到尺度今后 就能够摆放自己的方位和巨细了,巨细就用丈量出来的
        layout(placeable.width, placeable.height) {
            // 方位 就设置偏移量 即可
            placeable.placeRelative(0, 0)
        }
    })
    Text(
        text = "hello world"
    )
}

这儿的2个Text 在实际展现上 方位和巨细都一样,没有差异,放这个例子 其实便是为了给咱们展现一下 layout的效果,实际上关于Modifer.size ,padding 等扩展函数 中,他们的内部完结 都是与这个layout 休戚相关的

略微分析一下上述的程序

Jetpack Compose - LayoutModifier (十二)

Jetpack Compose - LayoutModifier (十二)

由上图可知,咱们传递进去的layout的lamda 他的返回值 必须得是 MeasureResult 这个接口类型的目标

能够看下这个接口, 他有宽高这2个咱们熟悉的参数,还有 alignmentlines 这个有点像文字制作时分的baseline的概念, 先跳过 不处理, 别的还有一个placeChildren的函数, 这个函数的效果 前面也说到过了,便是摆放自己的方位 ,这4个要素缺一不可,

回到咱们最初的程序中

Jetpack Compose - LayoutModifier (十二)

咱们在lambda 内部调用的layout这个函数 留意看 他是归于measureScope的 不要和Modifier的layout搞混,这个 内部调用的layout函数,他默许帮咱们返回了一个MeaureResult的目标, 便利就便利在这个目标 他默许帮咱们完结了 alignmentLines 与 placeChildren函数

有人可能会疑惑 这儿的 measure和layout和 传统自定义view的measure layout 好像啊,这儿的确很像,可是细节上不是一个东西

比方说layout,传统自定义view中的layout 是专归于viewgroup的,你能够重写layout函数,摆放你所有子view的方位

而在compose的Modifier的layout中 你是无法摆放你子view的方位的

测验在layout中完结padding的效果

咱们有了第一小节的根底 就能够来测验的完结 一个padding的效果

setContent {
    Column(Modifier.fillMaxWidth()) {
        Text(text = "hello world",
            Modifier.background(Color.Yellow)
                .layout { measurable, constraints ->
                    val padding = 10.dp.roundToPx()
                    // 能够了解为 传统view 中丈量自己巨细的步骤 获得自己宽高的尺度
                    val placeable = measurable.measure(
                        constraints.copy(
                            maxWidth = constraints.maxWidth + padding * 2, maxHeight = constraints.maxHeight + padding * 2
                        )
                    )
                    // 拿到尺度今后 就能够摆放自己的方位和巨细了,巨细就用丈量出来的
                    layout(placeable.width + padding * 2, placeable.height * 2) {
                        // 方位 就设置偏移量 即可
                        placeable.placeRelative(padding, padding)
                    }
                }
                )
        Text(
            text = "hello world", modifier = Modifier.background(Color.Red)
        )
    }
}

image.png

LayoutModifier 对布局的影响

前面咱们仿照了Modifier的padding完结,现在来看下 真实的padding 在compose中是怎样做的

Jetpack Compose - LayoutModifier (十二)

Jetpack Compose - LayoutModifier (十二)

能够看到这儿的要害是LayoutModifier接口

Jetpack Compose - LayoutModifier (十二)

这儿有人会问,在传统的view 系统中,是一个树形结构的view树,那在compose中是啥? 在compose中也是一个树形结构, 可是那不是view树了,而是一个LayoutNode

在传统view系统中,通常是measure和layout 来相互配合 终究渲染出咱们想要的成果,同样的,在LayoutNode中 也有类似的函数 remeasure 与 relayout

能够一步步跟一下核心的代码

Jetpack Compose - LayoutModifier (十二)

Jetpack Compose - LayoutModifier (十二)

Jetpack Compose - LayoutModifier (十二)

Jetpack Compose - LayoutModifier (十二)

这个measure办法 是个interface办法

他的具体完结其实便是

Jetpack Compose - LayoutModifier (十二)

留意这儿的完结类是InnerPlaceable ,咱们后边还会再遇到他

这个measureResult 也便是丈量成果 就会在布局的流程中 调用

了解了大约的measure流程,咱们就能够开端分析一下 LayoutModifier究竟对Compose的界面制作有什么效果?

那么多的LayoutModifier 比方padding,size等等,他们的先后顺序 对Compose的界面制作影响怎样?

咱们首要能够重视一下LayoutNode的 modifier这个特点

Jetpack Compose - LayoutModifier (十二)

尤其是他的set办法, 这个就很要害了,咱们每次给一个compose的组件 设置他的 modifier特点的时分 都会走到这个办法里边

细心看一下这个函数,要害点在红框内:

Jetpack Compose - LayoutModifier (十二)

咱们首要看一下这个foldout函数,他的意思便是 把modifier这个链表从右到左 进行遍历

怎样了解这句话

看下面的使用办法,所谓从右到左遍历,便是先读background 然后读padding 终究读fillmaxwidth

Jetpack Compose - LayoutModifier (十二)

再看第二个红框中的代码:

Jetpack Compose - LayoutModifier (十二)

这儿咱们首要重视那个最大的红框,这儿其实便是当判断了mod 是layoutModifier今后 就会走这个大红框的逻辑

大红框的逻辑很简单,其实便是 创建一个ModifidLayoutNode 或者 重用之前的ModifidLayoutNode

这个mod是啥?这个mod便是咱们设置的哪些LayoutModifier,

towrap是啥? 诶,这个当地就很要害了,咱们之前提过,这儿的遍历是从右往左进行遍历, 这个towrap

便是之前那次遍历的成果, 这个看一下这个lambda的终究表达式就很清楚了

有人这儿就要问了,towrap 是之前那次遍历的成果,那第一次遍历呢? 第一次遍历

怎样取之前的遍历成果?

其实关于第一次遍历来说,这个towrap的值便是foldOut的参数 这个inneraLayoutNodeWrapper

他其实便是InnerPlaceable

Jetpack Compose - LayoutModifier (十二)

InnerPlaceable 这个其实便是之前咱们读过的代码 负责measure的

所以咱们了解这段代码今后 他的意义便是

从右往左来遍历这个modifier,关于layoutModifier来说,最右边的在最里边,最左面的在最外面,

再换句话说,

假如咱们写了一个Compose的组件,比方是一个Text组件,

当咱们没有给这个组件 设置LayoutModifier参数时,他丈量的成果便是Text自己的丈量成果 也便是那个InnerPlaceable

当咱们给这个组件设置了一个LayoutModifier参数时,他丈量的成果便是 ModifiedLayoutNode , 而这个node 里边包裹的便是InnerPlaceable

以此类推。。。。。。。

所以现在问题的要害便是 看一下ModifierLayoutNode 是怎样measure的吧

Jetpack Compose - LayoutModifier (十二)

要害就在 这个measure办法了,咱们点进去看一下

Jetpack Compose - LayoutModifier (十二)

终究仍是回到了LayoutModifier这个接口的measure办法里了

有人要问了,知道这么细有啥用呢,我只想知道modifier有哪些api 能完结哪些作业呀, 别急,有了上面的根底 咱们能够看一下 下面几个例子了

比方下面这个代码:

Text(
    text = "hello world", modifier = Modifier.padding(10.dp).padding(20.dp).background(Color.Red)
)

你能看出来 这个代码的padding终究的效果是多少吗?是10仍是20?仍是其他的值?

答案是30.dp

为啥?

由于这段代码的意义其实便是 最外层有一个10.dp的 LayoutModifer,里边有一个20.dp的layoutModifer,这个20.dp的layoutModifier里边还有一个text自身的measure,所以终究便是一个30padding的 text组件 仅此而已了

再看一段代码

Text(
    text = "hello world", modifier = Modifier.padding(10.dp).size(20.dp).background(Color.Red)
)

你说这个代码 会不会由于有padding 而导致 咱们这个text组件的文字展现不全?

当然不会,由于这儿终究效果是 最外层是一个10.dp的layout,里边包裹着一个 size为20的layout, 这个size为20的layout里边 包裹了一个text, 你搞清楚这个逻辑 当然就能够容易的得出结论 上述的写法不会导致 文字展现不全

再看一下

Box(modifier = Modifier.size(20.dp).size(50.dp).background(Color.Red)
)

你说上面这个代码 终究的box的巨细是 20仍是50 仍是70?

答案当然是20,为啥? 由于最外层是一个20.dp的layout,里边包裹着一个50dp的layout, 没用啊,由于你最外层就20.dp

这句话还能够了解为, 我想要50.dp的巨细,可是我的父layout告诉我我说 你只有20.dp的巨细了 那我也就只能用20.dp的巨细了

别的要留意的是,还有个api 叫requiresize 他和size是有差异的

Box(modifier = Modifier.size(20.dp).requiredSize.(50.dp).background(Color.Red)
)

比方这种,他用的是requiresize 意思便是我就用50.dp的巨细, 尽管最外层就给了我20.dp的巨细,不要紧由于我要的是50 你给我20 ,那多出来的部分就被裁切掉了

而假如都是size 则不会呈现裁切的情况了