共计 3053 个字符,预计需要花费 8 分钟才能阅读完成。
留神,Jetpack Compose 中的控件被定义成一个一个的可组合函数,官网称这些控件为 Composable,翻译成中文是“可组合项”,当强调它作为一个界面的一部分呈现时,我会应用“控件”或“元素”之类的术语,要留神这三者之间的差异,我不晓得有没有更好的词,所以我只能用这两个。当仅仅强调它是一个可组合项时,我会失常应用“可组合项”这个术语。
Layout 零碎
根本准则
元素须要通过一些束缚来测量本人,这限度了一个元素的最大和最小的宽高。如果一个元素有子元素,那么它会测量每一个子元素来帮忙决定本人的大小,每当一个元素向父元素报告了它本人的大小时,那么它就失去了绝对于本身来搁置本人的子元素的机会。
compose 不容许屡次测量,和 Flutter 一样,起因就是反复测量作用于 UI 这种树形构造的是时候会带来指数级的性能降落。当然有很多时候你须要反复获取子元素的一些信息,这会有其它的方法。
自定义 layout modifier
在 compose 中,Modifier 提供了一系列函数,应用它们能够提供很多布局上的参数,比方 padding 等信息,通过自定义 modifier 来看下它是怎么工作的。
通过扩大函数来扩大 Modifier 中的办法,因为 modifier 是链式调用的,咱们扩大的办法也应该合乎链式调用规定,Modifier.then 办法用来辅助实现链式调用,它承受一个 Modifier,返回一个与这个 Modifier 联合后的 Modifier。
fun Modifier.firstBaselineToTop(firstBaseLineToTop: Dp) = this.then(
layout { measurable, constraints ->
// do something...
}
)
外面的这个 layout 也是一个 Modifier 中的办法,它承受一个参数,这个参数是一个 lambda,一会再说,这个 lambda 外面就是咱们进行测量和摆放子控件的中央。
measurable:被摆放的子控件
constraints:子控件的最大和最小宽高限度
上面实现一个这个成果,能够通过咱们扩大的 firstBaselineToTop 办法,设置文字的 FristBaseline 与顶部的间距。
第一步,咱们须要测量这个子控件,取得一个 Placeable 对象,咱们能够通过这个 Placeable 对象,绝对于父控件的地位来摆放这个子控件。
layout { measurable, constraints ->
val placeable = measurable.measure(constraints = constraints)
}
这里能够将给定的 constraints 限度间接传入,也能够本人结构。
当初这个子控件曾经依据给定的限度被测量好,下一步,咱们就须要计算它离顶部的高度,这里应该应用用户传入的高度减去 FirstBaseline 的地位,失去的就是这个控件应该离顶部的高度。
// 检测子元素是否有 FirstBaseLine
check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
val firstBaseLine = placeable[FirstBaseline]
// 计算元素该被搁置到的 Y 坐标,并减少元素的高度
val placeableY = firstBaseLineToTop.roundToPx() - firstBaseLine
val height = placeable.height + placeableY
万事俱备,该摆放这个控件了。
应用 MeasureScope.layout 办法向内部报告大小,并摆放本人,这个办法会返回一个 MeasureResult,正好是内部整个 lambda 表达式所要求的返回值。
layout(placeable.width,height) {placeable.placeRelative(0,placeableY)
}
残缺代码:
fun Modifier.firstBaselineToTop(firstBaseLineToTop: Dp) = this.then(
layout { measurable, constraints ->
val placeable = measurable.measure(constraints = constraints)
check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
val firstBaseLine = placeable[FirstBaseline]
val placeableY = firstBaseLineToTop.roundToPx() - firstBaseLine
val height = placeable.height + placeableY
layout(placeable.width,height) {placeable.placeRelative(0,placeableY)
}
}
)
@Preview
@Composable
fun useFirstBaselineToTop() {
Column {Text("Hi,there", modifier = Modifier.firstBaselineToTop(24.dp))
}
}
@Preview
@Composable
fun usePadding() {
Column {Text("Hi,there",modifier = Modifier.padding(top = 24.dp))
}
}
自定义 Layout
上面是自定义的一个简略的 Column 布局。因为和自定义 Modifier 差不多,不多说了。
@Composable
fun CustomColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit) {Layout(modifier = Modifier, content = content) { measurables, constraints ->
// 测量每一个子组件
val placeables = measurables.map {it.measure(constraints = constraints)
}
// yPos 用于记录以后组件的 y 地位
var yPos = 0
// 向父元素报告大小,这里和父元素一样大
layout(constraints.maxWidth,constraints.maxHeight) {
// 搁置每一个子元素
placeables.forEach { placeable ->
placeable.placeRelative(0,yPos)
yPos += placeable.height
}
}
}
}
总结
能够看出 Jetpack Compose 面向组合实现 UI 树相较于传统 View 模式的灵活性。
总的来说就是如下几点:
1. 子控件的每个 modifier 工作,进行测量,并且摆放(下一个 modifier 会在上一个的根底上进行测量摆放,这也是为什么 modifier 对程序敏感)
2. 父控件测量子控件的大小,(这个大小是子控件本人上报的,我集体感觉应该是 modifier 链中的最初一个 layout,其余的 layout 是向下一个 modifier 上报,只是集体高见,不对还望指出),依据这些大小计算出本人该有的尺寸,并上报给父控件的父控件,最初依照本人外部安顿好的程序对子元素进行摆放。
结语:后续会继续更新哦,喜爱就点赞关注一下吧。
相干视频
【Android 进阶】Compose 函数式编程重点剖析