在上一篇文章深入Jetpack Compose——布局原理与自定义布局(一) - 掘金 (juejin.cn) 中,我们大致了解了Layout过程并简单实现了两个自定义布局。本次让我们将目光转向Modifier和固有特性测量
本文部分参考自Android官方视频:Deep dive into Jetpack Compose layouts
关于Modifier的本质,RugerMc
大佬在图解 Modifier 实现原理 ,竟然如此简单这篇文章中已经解释地非常清楚了,我就不画蛇添足了。不过为了后续行文方便,我还是在此简单说几点:
伴生对象 Modifier
、内部子接口Modifier.Element
和CombinedModifier
。伴生对象Modifier
是日常使用最多的,后面两者均为内部实现,实际开发中无需关注Modifier.xxx()
方法实际上会创建一个Modifier
接口的实现类的实例。如Modifier.size()
会创建SizeModifer
实例@Stable
fun Modifier.size(size: Dp) = this.then(
SizeModifier(
/*省略具体细节*/
)
)
上面文章中的一张图来说明:
我们可以简单遍历一下看看。例如:
@Composable
fun TraverseModifier() {
val modifier = Modifier
.size(40.dp)
.background(Color.Gray)
.clip(CircleShape)
LaunchedEffect(modifier){
// 顺序遍历Modifier
modifier.foldIn(0){ index , element : Modifier.Element ->
Log.d(TAG, "$index -> $element")
index + 1
}
}
}
它的输出为:
0 -> androidx.compose.foundation.layout.SizeModifier@78000000
1 -> Background(color=Color(...), brush=null, alpha = 1.0, shape=RectangleShape)
2 -> SimpleGraphicsLayerModifier(...)
接下来,我们看看Modifier是怎么在布局中起作用的
先看一个例子
@Composable
fun ModifierSample1() {
// 父元素
Box(modifier = Modifier
.width(200.dp)
.height(300.dp)
.background(Color.Yellow)){
// 子元素
Box(modifier = Modifier
.fillMaxSize()
.wrapContentSize(align = Alignment.Center)
.size(50.dp)
.background(Color.Blue))
}
}
它实际显示的效果如下
(左上角的圆角是屏幕边缘)
我们来逐步看看这到底是怎么发生的。这里我们选择子元素,也就是那个小一点的蓝色Box,来看看它的measure和place过程。
首先是measure。父元素明确了自身大小为200*300,该大小也就是子元素能占据的最大空间。因此
最后,在Modifier
的一顿操作之下,Box会收到一个 w:50-50, h:50-50 的约束。到这里走过的状态如下:
接下来,Box内部的Layout
微件执行measure方法得到了自己的大小:50*50。这个大小反向传回到Modifier链的最后一项,并开始place。接下来:
这个过程很类似于Layout
微件,区别就是每个Modifier只有一个子元素(也就是Modifier链上的下一个元素)。事实上,如果看代码,你也很容易感受到二者的相似之处
拿wrapContentSize
修饰符的代码举例,其实现类WrapContentModifier
的measure
方法如下
fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints
): MeasureResult {
// 设置约束
val wrappedConstraints = Constraints(/* */)
// 测量得到可放置项
val placeable = measurable.measure(wrappedConstraints)
val wrapperWidth = placeable.width.coerceIn(constraints.minWidth, constraints.maxWidth)
val wrapperHeight = placeable.height.coerceIn(constraints.minHeight, constraints.maxHeight)
// layout函数放置到指定位置并返回结果
return layout(
wrapperWidth,
wrapperHeight
) {
val position = alignmentCallback(
IntSize(wrapperWidth - placeable.width, wrapperHeight - placeable.height),
layoutDirection
)
placeable.place(position)
}
}
怎么样,是不是很相似?
阅读量:167
点赞量:0
收藏量:0