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 休戚相关的
略微分析一下上述的程序
由上图可知,咱们传递进去的layout的lamda 他的返回值 必须得是 MeasureResult 这个接口类型的目标
能够看下这个接口, 他有宽高这2个咱们熟悉的参数,还有 alignmentlines 这个有点像文字制作时分的baseline的概念, 先跳过 不处理, 别的还有一个placeChildren的函数, 这个函数的效果 前面也说到过了,便是摆放自己的方位 ,这4个要素缺一不可,
回到咱们最初的程序中
咱们在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)
)
}
}
LayoutModifier 对布局的影响
前面咱们仿照了Modifier的padding完结,现在来看下 真实的padding 在compose中是怎样做的
能够看到这儿的要害是LayoutModifier接口
这儿有人会问,在传统的view 系统中,是一个树形结构的view树,那在compose中是啥? 在compose中也是一个树形结构, 可是那不是view树了,而是一个LayoutNode树
在传统view系统中,通常是measure和layout 来相互配合 终究渲染出咱们想要的成果,同样的,在LayoutNode中 也有类似的函数 remeasure 与 relayout
能够一步步跟一下核心的代码
这个measure办法 是个interface办法
他的具体完结其实便是
留意这儿的完结类是InnerPlaceable ,咱们后边还会再遇到他
这个measureResult 也便是丈量成果 就会在布局的流程中 调用
了解了大约的measure流程,咱们就能够开端分析一下 LayoutModifier究竟对Compose的界面制作有什么效果?
那么多的LayoutModifier 比方padding,size等等,他们的先后顺序 对Compose的界面制作影响怎样?
咱们首要能够重视一下LayoutNode的 modifier这个特点
尤其是他的set办法, 这个就很要害了,咱们每次给一个compose的组件 设置他的 modifier特点的时分 都会走到这个办法里边
细心看一下这个函数,要害点在红框内:
咱们首要看一下这个foldout函数,他的意思便是 把modifier这个链表,从右到左 进行遍历
怎样了解这句话
看下面的使用办法,所谓从右到左遍历,便是先读background 然后读padding 终究读fillmaxwidth
再看第二个红框中的代码:
这儿咱们首要重视那个最大的红框,这儿其实便是当判断了mod 是layoutModifier今后 就会走这个大红框的逻辑
大红框的逻辑很简单,其实便是 创建一个ModifidLayoutNode 或者 重用之前的ModifidLayoutNode
这个mod是啥?这个mod便是咱们设置的哪些LayoutModifier,
towrap是啥? 诶,这个当地就很要害了,咱们之前提过,这儿的遍历是从右往左进行遍历, 这个towrap
便是之前那次遍历的成果, 这个看一下这个lambda的终究表达式就很清楚了
有人这儿就要问了,towrap 是之前那次遍历的成果,那第一次遍历呢? 第一次遍历
怎样取之前的遍历成果?
其实关于第一次遍历来说,这个towrap的值便是foldOut的参数 这个inneraLayoutNodeWrapper
他其实便是InnerPlaceable
InnerPlaceable 这个其实便是之前咱们读过的代码 负责measure的
所以咱们了解这段代码今后 他的意义便是
从右往左来遍历这个modifier,关于layoutModifier来说,最右边的在最里边,最左面的在最外面,
再换句话说,
假如咱们写了一个Compose的组件,比方是一个Text组件,
当咱们没有给这个组件 设置LayoutModifier参数时,他丈量的成果便是Text自己的丈量成果 也便是那个InnerPlaceable
当咱们给这个组件设置了一个LayoutModifier参数时,他丈量的成果便是 ModifiedLayoutNode , 而这个node 里边包裹的便是InnerPlaceable
以此类推。。。。。。。
所以现在问题的要害便是 看一下ModifierLayoutNode 是怎样measure的吧
要害就在 这个measure办法了,咱们点进去看一下
终究仍是回到了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 则不会呈现裁切的情况了