" Jetpack Compose - - Modifier 系列文章 "
📑 《 深入解析 Compose 的 Modifier 原理 - - Modifier、CombinedModifier 》
📑 《 深度解析 Compose 的 Modifier 原理 - - Modifier.composed()、ComposedModifier 》
📑 《 深入解析 Compose 的 Modifier 原理 - - Modifier.layout()、LayoutModifier 》
📑 《 深度解析 Compose 的 Modifier 原理 - - DrawModifier 》
📑 《 深度解析 Compose 的 Modifier 原理 - - PointerInputModifier 》
📑 《 深度解析 Compose 的 Modifier 原理 - - ParentDataModifier 》
其实原理性分析的文章,真的很难讲的通俗易懂,讲的简单了就没必要写了,讲的繁琐难懂往往大家也不乐意看,所以只能尽量想办法,找个好的角度(比如从 Demo 代码示例出发)慢慢带着大家去钻源码,如果确实能帮助到大家完全理解了文章所讲述到的源码理论,那就值了。
在正式开始分析 DrawModifier 之前,建议你先看看 【LayoutModifier 和 Modifier.layout 用法及原理】这篇文章,毕竟它是作为 Modifier 原理解析的第一篇文章,对你了解整个 Modifier 架构还是很有帮助的,或者说它是最基础的一篇文章,如果不熟悉,后面的系列 Modifier 你可能会看的比较费劲… …
ParentDataModifier 的作用
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
Row() {
Box(Modifier.size(40.dp).background(Color.Red))
Box(Modifier.size(40.dp).background(Color.Green))
Box(Modifier.size(40.dp).background(Color.Blue))
}
}
}
}
}
这段代码很简单,横向布局显示三个 Box,效果如下:
如果我希望这三个方块可以平分横向布局的空间,该怎么做?
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeBlogTheme {
Row() {
Box(Modifier.size(40.dp).background(Color.Red).weight(1f))
Box(Modifier.size(40.dp).background(Color.Green).weight(1f))
Box(Modifier.size(40.dp).background(Color.Blue).weight(1f))
}
}
}
}
}
只要给每个 Box 添加 Modifier.weight(1f) 即可,相当于传统布局里面 LinearLayout 的 android:layout_weight。
从效果来看,Modifier.weight() 与测量布局有关,那说明它内部是一个 LayoutModifier?我们来看下:
interface RowScope {
@Stable
fun Modifier.weight(
/*@FloatRange(from = 0.0, fromInclusive = false)*/
weight: Float,
fill: Boolean = true
): Modifier
}
internal object RowScopeInstance : RowScope {
@Stable
override fun Modifier.weight(weight: Float, fill: Boolean): Modifier {
require(weight > 0.0) { "invalid weight $weight; must be greater than zero" }
return this.then(
LayoutWeightImpl(
... ...
)
)
}
}
then 套了一个 LayoutWeightImpl:
internal class LayoutWeightImpl(
val weight: Float,
val fill: Boolean,
inspectorInfo: InspectorInfo.() -> Unit
) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
... ...
}
原来 Modifier.weight 是创建了一个 ParentDataModifier,而不是 LayoutModifier,难道 ParentDataModifier 是 LayoutModifier 的一个子接口?
interface ParentDataModifier : Modifier.Element {
fun Density.modifyParentData(parentData: Any?): Any?
}
很明显,ParentDataModifier 就只继承了 Modifier.Element 接口,与 LayoutModifier 无关。
ParentDataModifier 实际上是一个辅助的 Modifier,用于给外层包裹的 Composable 使用的,更具体讲是提供给被设置的子组件的父组件在测量子组件的时候使用,让父组件能更好的测量子组件。
Row() {
// ParentDataModifier 是提供给 Row 使用的帮助测量 Box
Box(Modifier.size(40.dp).background(Color.Red).weight(1f))
Box(Modifier.size(40.dp).background(Color.Green).weight(1f))
Box(Modifier.size(40.dp).background(Color.Blue).weight(1f))
}
而 LayoutModifier 它只能作用于单一的组件,即对单一的组件处理测量布局,比如上面例子的 Box(),对 Box() 设置的 LayoutModifier 只会它自己生效。
Row() {
// 三个组件都设置了 Modifier.weight(),依靠单一组件无法测量出需要多少宽度
// 需要互相知道组件宽度才能确定自己宽度
Box(Modifier.size(40.dp).background(Color.Red).weight(1f))
Box(Modifier.size(40.dp).background(Color.Green).weight(1f))
Box(Modifier.size(40.dp).background(Color.Blue).weight(1f))
}
为了能测量出子组件所需要的宽度/高度,Compose 提供了 ParentDataModifier 方案,让父组件协助计算测量。
所以:ParentDataModifier 虽然设置给子组件,但却是提供给父组件使用的数据,辅助子组件进行尺寸测量和绘制计算的。