从分析 Touch 事件分发就可以看出 AndroidComposeView 是原生 View 体系和 Compose 的中间件。原生 View 体系中的事件传递到 AndroidComposeView ,AndroidComposeView 再传递到 AndroidComposeView.root 的 Compose 树中开启 Compose 事件处理流程。
Compose 测量流程也是如此
结合上图可以看出 Compose 测量流程从 AndroidComposeView#onMeasure() 方法开始:
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
trace("AndroidOwner:onMeasure") {
if (!isAttachedToWindow) {
invalidateLayoutNodeMeasurement(root)
}
val (minWidth, maxWidth) = convertMeasureSpec(widthMeasureSpec)
val (minHeight, maxHeight) = convertMeasureSpec(heightMeasureSpec)
//计算最大最小宽高
val constraints = Constraints(minWidth, maxWidth, minHeight, maxHeight)
if (onMeasureConstraints == null) {
// first onMeasure after last onLayout
onMeasureConstraints = constraints
wasMeasuredWithMultipleConstraints = false
} else if (onMeasureConstraints != constraints) {
// we were remeasured twice with different constraints after last onLayout
wasMeasuredWithMultipleConstraints = true
}
//设置 Compose 树根节点 最大最小宽高 约束
measureAndLayoutDelegate.updateRootConstraints(constraints)
//对 Compose 树进行测量、布局
measureAndLayoutDelegate.measureAndLayout()
//设置 AndroidComposeView 测量后的宽高
setMeasuredDimension(root.width, root.height)
//如果 AndroidComposeView 存在 Android 原生 View
if (_androidViewsHandler != null) {
//对 Android 原生 View 进行测量
androidViewsHandler.measure(
MeasureSpec.makeMeasureSpec(root.width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(root.height, MeasureSpec.EXACTLY)
)
}
}
}
internal class AndroidComposeView(context: Context) :
ViewGroup(context), Owner, ViewRootForTest, PositionCalculator, DefaultLifecycleObserver {
override val root = LayoutNode().also {
it.measurePolicy = RootMeasurePolicy
it.modifier = Modifier
.then(semanticsModifier)
.then(_focusManager.modifier)
.then(keyInputModifier)
it.density = density
}
private val measureAndLayoutDelegate = MeasureAndLayoutDelegate(root)
}
在 AndroidComposeView#onMeasure() 中 Compose 不在使用原生 View 的测量方法,而是由 MeasureAndLayoutDelegate 代理去完成整个 Compose 树的测量和布局。自此测量流程从 View 体系转入 Compose 体系。
为了更好的分析分析测量流程我们需要先对 LayoutNode 有所了解(接下来只分析跟测量流程相关的部分)
Compose 函数转换成 LayoutNode
Compose 中的 LayoutNode 相当于原生 View 体系中的 View , 所有的 Compose 组件都会转换成 LayoutNode 对象添加到 root 为根节点的 Compose 树中。
Compose 中提供的组件都是基于 Layout 或 SubcomposeLayout 函数实现的。
- Layout :封装 ReusableComposeNode 函数,子组件测量时受 Layout 约束影响。例如:Column 、Row
- SubcomposeLayout : 封装 ComposeNode 函数,子组件测量时除了受 SubcomposeLayout 约束影响外,测量结果还可以影响其他子组件测量。例如 LazyColumn
约束: LayoutNode 的最大/最小宽高 , 在 Constraints.kt 中定义。
ReusableComposeNode 和 ComposeNode 都通过 Composer 将组件转换成 LayoutNode 添加到 Compose 树中,转换的主要流程的是一样的,具体差异我们先不去研究。
以 Layout 为例来分析转换过程
@Suppress("ComposableLambdaParameterPosition")
@Composable inline fun Layout(
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
val viewConfiguration = LocalViewConfiguration.current
ReusableComposeNode<ComposeUiNode, Applier<Any>>(
//() -> ComposeUiNode = LayoutNode.Constructor
//factory 函数就是 LayoutNode 构造函数
factory = ComposeUiNode.Constructor,
//执行时会依次设置 LayoutNode 中下面四个属性的值
update = {
set(measurePolicy, ComposeUiNode.MeasurePolicy)
set(density, ComposeUiNode.SetDensity)
set(layoutDirection, ComposeUiNode.SetLayoutDirection)
set(viewConfiguration, ComposeUiNode.SetViewConfiguration)
},
// materializerOf -> set(materialized, ComposeUiNode.SetModifier)
// 执行时设置 LayoutNode 中 modifier 属性的值
skippableUpdate = materializerOf(modifier),
//@Composable () -> Unit 子组件的 Compose 函数
content = content
)
}
//LayoutNode 实现 ComposeUiNode 接口, 上面代码执行时就是 LayoutNode 调用对应的方法
@PublishedApi
internal interface ComposeUiNode {
var measurePolicy: MeasurePolicy
var layoutDirection: LayoutDirection
var density: Density
var modifier: Modifier
var viewConfiguration: ViewConfiguration
companion object {
// LayoutNode.Constructor
// internal val Constructor: () -> LayoutNode = { LayoutNode() }
val Constructor: () -> ComposeUiNode = LayoutNode.Constructor
val SetModifier: ComposeUiNode.(Modifier) -> Unit = { this.modifier = it }
val SetDensity: ComposeUiNode.(Density) -> Unit = { this.density = it }
val SetMeasurePolicy: ComposeUiNode.(MeasurePolicy) -> Unit =
{ this.measurePolicy = it }
val SetLayoutDirection: ComposeUiNode.(LayoutDirection) -> Unit =
{ this.layoutDirection = it }
val SetViewConfiguration: ComposeUiNode.(ViewConfiguration) -> Unit =
{ this.viewConfiguration = it }
}
}
上面只是传参调用,下面是 ReusableComposeNode 函数的执行流程
@Composable
inline fun <T : Any?, reified E : Applier<*>> ReusableComposeNode(
noinline factory: () -> T,
update: @DisallowComposableCalls Updater<T>.() -> Unit,
content: @Composable () -> Unit
) {
if (currentComposer.applier !is E) invalidApplier()
currentComposer.startReusableNode()
if (currentComposer.inserting) {
//执行 LayoutNode 构造方法并将生成的对象添加到 Compose 树中
currentComposer.createNode(factory)
} else {
currentComposer.useNode()
}
currentComposer.disableReusing()
//执行 update 方法设置 LayoutNode 对象的四个属性
Updater<T>(currentComposer).update()
currentComposer.enableReusing()
//设置 LayoutNode 对象的 modifier 属性
SkippableUpdater<T>(currentComposer).skippableUpdate()
currentComposer.startReplaceableGroup(0x7ab4aae9)
//执行子组件的 Compose 函数
content()
currentComposer.endReplaceableGroup()
currentComposer.endNode()
}
转换过程主要分为下面四个步骤:
- 生成 LayoutNode 对象,并添加到 Compose 树中
- 设置 LayoutNode 对象的 measurePolicy、density、layoutDirection、viewConfiguration 属性
- 设置 LayoutNode 对象的 modifier 属性
- 继续转换子组件的 Compose 函数(基础组件例如 BasicText 或 Image 的 content 都是空函数)
1 生成 LayoutNode :调用无参构造生成 LayoutNode 对象,完成对象初始化。
MeasurePolicy
接口中声明了五个方法。其中四个方法有默认实现,作用是支持固有特性测量返回对应的最大/最小宽高。
具体的测量策略需要重写 MeasureScope.measure 方法,测量子元素与容器本身的大小并决定如何在容器中布局子元素。
fun MeasureScope.measure(
//实现 Measurable 接口的子元素,一般是 LayoutNode
measurables: List<Measurable>,
//容器最大/最小宽高约束
constraints: Constraints
): MeasureResult //测量后容器的大小,及子元素布局函数
自定义布局
前面提到过 Compose 中提供的组件都是基于 Layout 或 SubcomposeLayout 函数实现的。 通过下面四个步骤自定义一个简单的自动换行标签容器来熟悉 MeasurePolicy 使用。
- 根据传入的约束(constraints)测量子元素(measurables)的大小
- 根据约束和子元素的大小确定容器(Layout)本身的大小
- 编写子元素在容器中布局的函数
- 将容器大小和布局函数封装到 MeasureResult 中返回
编码时会将 3、4 步使用 layout() 函数合并在一起。
@Composable
fun TagLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Layout(modifier = modifier, content = content) { measurables, constraints ->
var usedWidth = 0
var height = 0
//1 :测量子组件,每个子组件测量后会返回该组件的 placeable 对象
val placeables = measurables.mapIndexed { index, measurable ->
val placeable = measurable.measure(constraints)
// 2 :根据子组件的测量值计算 TagLayout 的 高度,TagLayout 宽度使用容器的最大宽度
if (index == 0) {
height = placeable.measuredHeight
usedWidth = placeable.measuredWidth
} else {
//一行放不下 ,放到下一行 ,累加容器高度
if (usedWidth + placeable.measuredWidth > constraints.maxWidth) {
usedWidth = 0
height += placeable.measuredHeight
} else {
usedWidth += placeable.measuredWidth
}
}
placeable
}
//3、4 :使用 layout 函数,传入测量后的宽(constraints.maxWidth) 高(height)
//及子元素布局函数(lambda)生成返回的 measureResult
val measureResult = layout(constraints.maxWidth, height) {
var xOffset = 0
var yOffset = 0
placeables.forEach {
//如果需要换行将 xOffset,yOffset 设置成下一行起始的位置 ,
if (xOffset + it.measuredWidth > constraints.maxWidth) {
xOffset = 0
yOffset += it.measuredHeight
}
//将子组件左顶点放置到(x,y)处,参数 x ,y 是相对于容器左顶点(0,0)的偏移量
it.placeRelative(xOffset, yOffset)
// xOffset 累加
xOffset += it.measuredWidth
}
}
measureResult
}
}
@Composable
fun Tag(text:String){
val bgColor = Color.Magenta
Text(modifier = Modifier.padding(2.dp).background(bgColor, RoundedCornerShape(2.dp))
.padding(6.dp, 4.dp)
,text = text, color = contentColorFor(bgColor))
}
setContent {
TagLayout(modifier = Modifier.background(Color.Cyan)) {
Tag(text = "MeasurePolicy")
Tag(text = "constraints")
Tag(text = "placeables")
Tag(text = "measureResult")
Tag(text = "layout")
}
}
SetMeasurePolicy
接下来我们看 Compose 函数转换成 LayoutNode 时 setMeasurePolicy 都做了什么
internal class LayoutNode : Measurable, Remeasurement, OwnerScope, LayoutInfo, ComposeUiNode {
// set measurePolicy 相关
internal val intrinsicsPolicy = IntrinsicsPolicy(this)
override var measurePolicy: MeasurePolicy = ErrorMeasurePolicy
set(value) {
if (field != value) {
field = value
intrinsicsPolicy.updateFrom(measurePolicy)
requestRemeasure()
}
}
}
LayoutNode 初始化时会实例化 IntrinsicsPolicy 对象, IntrinsicsPolicy 对象将 LayoutNode 对象当前的 MeasurePolicy 保存到 State 中,提供 updateFrom() 方法更新 State 的值, 使得 LayoutNode 对象的 MeasurePolicy 变化后可以触发重组机制。
internal class IntrinsicsPolicy(val layoutNode: LayoutNode) {
private var measurePolicyState: MutableState<MeasurePolicy>? = null
private var pendingMeasurePolicy: MeasurePolicy? = null
fun updateFrom(measurePolicy: MeasurePolicy) {
if (measurePolicyState != null) {
measurePolicyState!!.value = measurePolicy
} else {
pendingMeasurePolicy = measurePolicy
}
}
//......
}
下一篇我们继续分析 Modifier 和 Compose 函数转化成 LayoutNode 后 SetModifier 都做了什么