本文字数:10501字
预计阅读时间:27 分钟
Compose自定义布局的使用
我们知道,在Android View体系下,自定义布局需要继承ViewGroup重写onMeasure、onLayout方法,那么在Compose UI框架中该如何实现自定义布局呢?
今天我们就来学习下Compose UI中自定义布局的具体使用。
实现目标
项目中有一个房源展示页面,用来展示一栋楼的所有房间信息,布局要求如下:
每个房间的宽高尺寸固定,水平方向需要动态计算可以显示的房间数量,内容水平方向居中;
房源数量较少,无法充满屏幕时,房源在屏幕中竖直方向居中显示;
房源数量超过屏幕时,从上向下布局,竖直方向可以滑动查看。
我们以此页面为目标,学习Compose自定义布局的使用。
效果图
效果一:上下、左右居中
效果二:上下滑动
Compose 自定义布局实现方式在编写代码前,我们先来了解下Compose 中自定义布局的实现方式。
Compose中使用Layout 可组合项来实现自定义布局,在Layout函数中完成子元素的测量和放置。以下是 Layout 可组合项的函数签名:
@Composable inline fun Layout(
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {}
一个自定义布局的Layout代码结构通常如下代码所示:
@Composable
fun MyBasicColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content
) { measurables, constraints ->
// 1. 使用给定的约束条件constraints测量children
val placeables = measurables.map { measurable ->
measurable.measure(constraints)
}
// 2. 设置布局的尺寸,放置子元素
layout(constraints.maxWidth, constraints.maxHeight) {
var yPosition = 0
//在父布局中放置children
placeables.forEach { placeable ->
placeable.placeRelative(x = 0, y = yPosition)
yPosition += placeable.height
}
}
}
}
从上面代码可以看到,Compose实现自定义布局,主要有两步:
测量每个子元素在父布局约束下的大小
确定布局尺寸,放置子元素
主要参数介绍
Layout函数中,有三个主要参数:
1. modifer
由外部传入的修饰符,用来修饰我们自定义的这Layout 组件的一些属性或约束 Constraints;
2. content
自定义布局 Layout 组件中所包含的子元素 children;
3. measurePolicy
mearsurePolicy 参数是 MeasurePolicy 类型,它是一个函数式接口,指定了布局测量和放置项目的方式。我们通常在Layout函数中以尾随 Lambda 的形式提供 MeasurePolicy 作为参数,从而实现所需的 MeasureScope.measure
函数。
fun interface MeasurePolicy {
fun MeasureScope.measure(
measurables: List<Measurable>,
constraints: Constraints
): MeasureResult
}
measure函数接受一个 Constraints 对象来告知 Layout 它的尺寸限制。Constraints 是一个简单类,用于限制 Layout 的最大和最小宽度与高度, constraints中提供的maxWidth和maxHeight是计算过modifier中padding之后的值, 所以布局中不需要再考虑padding:
class Constraints {
val minWidth: Int
val maxWidth: Int
val minHeight: Int
val maxHeight: Int
}
measure 函数还会接受 List<Measurable> 作为参数,表示的是传入的子元素, Mesurealbe中拥有测量元素尺寸的函数Mesurealbe.measure(constraints: Constraints)
,使用此函数完成子元素的测量工作,获取子元素的布局尺寸。
在MeasurePolicy的measure函数中,完成测量和放置子元素的过程。
1)测量
遍历measureables, 调用measure(constrains:Constrains)
方法进行测量。获取子元素的测量结果Placeable,Placeable包含测 量的宽度和高度
2)放置
调用layout(width: Int,height: Int,alignmentLines: Map<AlignmentLine, Int> = emptyMap(),placementBlock: Placeable.PlacementScope.() -> Unit)
方法对子元素进行布局。
width, height指定可组合项的布局尺寸, placementBlock是具体的布局流程。
测量后的Placeable表示为可布局对象。通过placeable.placeRelative(x:Int,y:Int)
方法对其进行摆放。x,y表示其距当前组件左上角的偏移量。另外还有一个place(x: Int, y: Int)
方法,两个方法的区别是:placeRelative方法支持RTL布局,也就是从右向左的布局,place方法只支持LTR布局。
代码实现
接下来,正式开始编写代码。
我们预先定义一些布局条件: