注册

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,大同小异大同小异。

到这里自定义布局就结束了,自定义布局难点主要还是在子view在父view约束下面的布局逻辑,也可以看到其实这个移植以前views下面的自定义布局应该是比较容易的,把坐标计算逻辑抽离,然后就可以轻松完成移植了。(另外我从这里还发现了compose下面怎么实现类似以前类的继承,那就是活用fun中最后一个lamda参数,由于kotlin语法的关系,容易把lamda看作是函数体,其实在fun中只是调用了一个fun,函数具体执行都被隐藏了起来)

0 个评论

要回复文章请先登录注册