在之前的【 聊聊 Jetpack Compose 原理 – LayoutModifier 和 Modifier.layout 】文章中,我们了解了 Modifier.layout() 修饰符的用法,并且探讨了它背后那个 LayoutModifier 的原理。
除了 Modifier.layout() 修饰符,Compose 还提供了一个叫 Layout 的 Composable 组件,可以直接在 Composable 函数中调用,方便自定义布局。
两者都是自定义布局,有什么区别?
Row():
@Composable
inline fun Row(
modifier: Modifier = Modifier,
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
verticalAlignment: Alignment.Vertical = Alignment.Top,
content: @Composable RowScope.() -> Unit
) {
val measurePolicy = rowMeasurePolicy(horizontalArrangement, verticalAlignment)
Layout(
content = { RowScopeInstance.content() },
measurePolicy = measurePolicy,
modifier = modifier
)
}
Column():
@Composable
inline fun Column(
modifier: Modifier = Modifier,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
content: @Composable ColumnScope.() -> Unit
) {
val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)
Layout(
content = { ColumnScopeInstance.content() },
measurePolicy = measurePolicy,
modifier = modifier
)
}
Layout() 自定义布局主要涉及三个工作:
老规矩,查看 Layout() 源码:
@Composable inline fun Layout(
content: @Composable @UiComposable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
... ...
}
Layout() 函数构成很简单:
初步了解了 Layout() 源码后,我们看看自定义 Layout() 怎么写?首先,content 参数是必备的,也就是你自定义的可组合项的 {} 表达式里面的内容。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
CustomLayout { // @@@ {} 内容作为 content 传入
// 子组件 1
// 子组件 2
// 子组件 3
}
}
}
}
}
@Composable
fun CustomLayout(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
// 开始测量和布局策略流程
Layout(content, modifier) {measurables, constraints ->
// 1. 子组件挨个测量自己,然后会把测量结果报告给他们的父组件
// 2. 父组件确定整体的宽高
layout(父组件的宽, 父组件的高) {
// 确定子组件的摆放位置
}
}
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
CustomLayout {
Box(Modifier.size(60.dp).background(Color.Red))
Box(Modifier.size(60.dp).background(Color.Yellow))
Box(Modifier.size(60.dp).background(Color.Blue))
}
}
}
}
}
@Composable
fun CustomLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Layout(content, modifier) {measurables, constarins ->
var width = 0
var height = 0
// 对自组件挨个进行一次测量
val placeables = measurables.map { measurable ->
measurable.measure(constarins).also { placeable ->
width = max(width, placeable.width)
height += placeable.height
}
}
layout(width, height) {
var totalHeight = 0
placeables.forEach {
it.placeRelative(0, totalHeight)
totalHeight += it.height
}
}
}
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
CustomLayout {
Box(Modifier.size(60.dp).background(Color.Red))
Box(Modifier.size(60.dp).background(Color.Yellow))
Box(Modifier.size(60.dp).background(Color.Blue))
}
}
}
}
}
@Composable
fun CustomLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Layout(content, modifier) {measurables, constarins ->
var width = 0
var height = 0
val placeables = measurables.map { measurable ->
measurable.measure(constarins).also { placeable ->
width += placeable.width
height = max(height, placeable.height)
}
}
layout(width, height) {
var totalWidth = 0
placeables.forEach {
it.placeRelative(totalWidth, 0)
totalWidth += it.width
}
}
}
}