Android compose自定义布局
开新坑了,compose自定义布局。基础知识不说了,直接上正题。
我们知道,在views体系下,自定义布局需要view集成viewgroup重写onMeasure、onLayout方法,在compse中,是使用Layout的compose方法,结构如下:
以一个自定义Column为例:
1、首先我们定义自己的cpmpose函数
@Composablefun
MyBasicColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
)
这个方法包含最基础的两个入参,一个是修饰符modifier,一个是@composable注解的lamda表达式作为子项的内容
2、看看具体函数体的操作
@Composable
fun MyBasicColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit) {
Layout(
modifier = modifier,
content = content
) { measurables, constraints ->
// Don't constrain child views further, measure them with given constraints
// List of measured children
val placeables = measurables.map { measurable ->
// Measure each children
measurable.measure(constraints)
}
// Set the size of the layout as big as it can
layout(constraints.maxWidth, constraints.maxHeight) {
// Track the y co-ord we have placed children up to
var yPosition = 0
// Place children in the parent layout
placeables.forEach { placeable ->
// Position item on the screen
placeable.placeRelative(x = 0, y = yPosition)
// Record the y co-ord placed up to
yPosition += placeable.height
}
}
}
}
代码是从官网抄的,正确性就不用说了,具体分析一下作用。
1、在函数体中使用已经定义好的Layout方法。(这个有点类似 类 的继承,compose中所有组件定义都是使用方法,没有类中的子类父类的概念,如果想要做一些统一的封装操作会比较麻烦,可以使用这种方法,函数体内去执行另一个封装好的函数,而函数最后一个参数使用@composable注解的lamda)Layout方法把修饰符和content接收,回调中发送的是 measurables和constraints.从名字就可以猜出这两个参数的作用
- measurables:可测量元素,就是传进来的子元素
- constraints:父类约束条件
Layout函数的lamda来自于第三个入参MeasurePolicy,这是一个接口,上面两个参数就来自于这个接口的回调:
fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult
2、使用map函数遍历measurables,每一个measurable调用measure并把constraint传入,相当于给每个子控件根据父控件的约束进行测量,类似于views体系下面的measure,得到palceables。
3、调用layout方法(注意小写,这是单独放置一个控件的方法,是单个可组合项的修饰,具体下面会再讲),layout(width,heigiht)传入布局的宽和高,在layout方法lamda中,对每一个placeable调用place方法(有几个类似的,这里使用placeRelative),传入相应坐标,完成子view的布局
到这里一个自定义布局就完成了,其实和views体系下面很像,也是相似的两步:
1、测量每个子view在父view约束下的大小
2、遍历子view,使用layout方法将每个view放在正确的位置上。
大同小异大同小异
3、关于layout(注意是小写的)
先抄一段官网的说明:
您可以使用 layout
修饰符来修改元素的测量和布局方式,layout 是一个 lambda;它的参数包括您可以测量的元素(以 measurable
的形式传递)以及该可组合项的传入约束条件(以 constraints
的形式传递)
再抄一段代码:
fun Modifier.firstBaselineToTop(
firstBaselineToTop: Dp) =
layout { measurable, constraints ->
// Measure the composable
val placeable = measurable.measure(constraints)
// Check the composable has a first baseline
check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
val firstBaseline = placeable[FirstBaseline]
// Height of the composable with padding - first baseline
val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
val height = placeable.height + placeableY
layout(placeable.width, height) {
// Where the composable gets placed
placeable.placeRelative(0, placeableY)
}}
这个是官网上面修改text baseline top padding的方法。具体内容就不说了,可以看到,是定义了一个modifier的扩展函数,回调参数是一个measurable,内部具体还是调用layout,大同小异大同小异。