Jetpack Compose 测量流程源码分析

「本文已参加好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你应战!」

本文你将学到什么

经过一段代码场景,以Layout 函数为进口分前端工程师析源码,答复一些心中所惑,如经过 Modifier 设置巨细是假定起效果的?MeasurePolicy 接口的 measappstoreure 办法是怎样调用的?布局中的丈量流程是什么样的?控件是怎前端工程师样招认大elementary小的?

回想

在 JetPack Compose 手写一个 Row 布局 | 自界说布局 一文中咱们现已了解了怎样自elements界说 Layout,运用 Layout 函数即可。

@Composable inline fun Layout(
content: @Celementanimationomposable () -> Unit,
m前端工程师odifier: Modifiappreciateer = Modifier,
measureelements是什么牌子Policy: MeasurePolicy
) {}

咱们可appetite以经过参数 modifier后端言语 给布局指定巨细,在后端开发薪酬一般多少 me后端开发薪酬一般多少asurePolicy 中对 childreappreciaten 进行丈量和布置,布局的 children 写elements翻译在 content 函数中。运用起来很便利嘛 ,但是好奇心让我想知道这个Layappetiteout里面做了些什么? 这儿面的源码或许很凌乱,但仍是想测验着看一看,不试一试怎样apple知道呢。后端开发是干什么的

为了便利探求和调试代码,本文以下面代码为场景进行剖析。

@Composable
private fun ParentLayout(moappointmentdifier: Modiapp是什么意思fier = Modifier, content: @Composable () -前端开发> Unit) {
//布局的丈量战略
val measurePolicy = MeasurePolicy { measurables, const后端开发raints后端开发薪酬一般多少 ->
//1.丈量 children
val p前端结构laceables = measurelementanimationables.后端是做什么的maappstorep { child ->
child.measure(constraielement滑板nts)
}
var xPosition = 0
//2.放置 children
layout(constraints.minWidth, constraints.minHeight) {
placeables.forEach { placeable ->
p后端言语laceable.place前端和后端的差异Relative(xPosition, 0)
xPosition += placeable.width
}
}
}
//代码剖析进口
Layout(content = c后端是做什么的ontent, modifier = modifier, measurePolicy = measurePolicy)
}
@Composable
private fun ChildLayout(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
//...代码和ParentLayout类似
}
cl后端工程师首要做什么ass MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstelementuianceState)
setContent {
ParentLayout(
Modif接口和抽象类的差异ier
.size(后端开发需求学什么100.dp)
.padding(10.dp)
.接口卡background(Color.Blue)
) {
ChildLayout {
Box {}
}
ChildLayout {}
}
}
}
}

本次探究希望能答复下面几个问题

  1. ParentLayout 中后端开发是干什么的 经过 modifier 设置巨细是怎样起到效果的 ?
  2. MeasurePolicy 接口的 measure 办法是怎样调用的?他的参数值是怎样来的呢?
  3. 布局后端开发需求学什么中的丈量流程是什么样的?

下面就带着上面这些问题,在看源码的进程中测验去阐明这些问题。

本文源码对应版别 compose_version = ‘1.0.0-rc01’

跟踪 Modifier & MeasurePolicy

为了便利跟踪代码,我来给代码接口的界说设置点跟踪器 (别搞丢了)
下面的代码中 modifier 参数达到的方位我会用 符号, measurePolicy 抵APP达的接口方位用 符号

Layout.kt → Layout 函数源码


@Compo前端学什么sable inline fun Layout(
content: @Composable () -> Unit,
modifier: Modifier = Modifier,
m后端开发需求学什么easurePolicy: Measu接口类型rePolicy
) {
val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current
ReusableComposeNode<ComposeUiNode, Applier<Any>>(
factory = ComposeUiNode.Constructor,
update = {
set(measurePolicy, ComposeUiNo后端开发薪酬一般多少de.SetMeasurePolicy) //    measurePolicy 在这
set(de后端组nsity, ComposeUiNode.SetDensity)
set(layoutD后端组irection, ComposeUiNode.SetLayoutDireelementanimationction)
},
skippableUpdate = m后端开发是干什么的aterializerOf(modifier), //    modifier 在这
content = content
)
}

从上面源码能够看出,Layout 函数体中没有做什么处理,核心内容便是调用 ReusableComposeNode 函数。

@Composable 注解的函数主张首字母大写已差异一般函数,看代码的时分总觉的前端 ReusableComposeNode 是个类,点进去发现它是个 C接口测验面试题omposabl接口是什么e 函数 。

Composables.kt → Re接口crc过错计数usableComposeNode 函数

inline fun <T, reified E : Applieelementanimationr<*>> ReusableCompappstoreoseNode(
noinline factory: () -> T,
update: @DisallowComposableCalls Updater<T>.(elementary) -> Unit,
noinline skippableUpdate: @Composable SkippablappointmenteUpdapplicationater<T>.() -> Unit,
content: @前端Compelementaryosable () -> Unit
) {
i后端不支持corsf (currentComp接口测验面试题oser.applier !is E) invalidApplier()
currentComposer.startNode()
if (currentelementary是什么意思Composer.inserting) {
curr后端不支持corsentComposer.createNode(factory)
} else {
currentComposer.u前端工程师seNode()接口
}
//实施update函数
Updater<T>(currentComposer).update() //    measurePolicy 在这
//实施skippableUpdate函数
SkippableUpdater<T>(currentComposer).skippableUpdate()  //    modelementary是什么意思ifieelementaryr 在这函数中
currentComposer.start前端技能ReplaceableGroup(0x7ab4aae9)
content()
currentComposer.endReplaceableGroup()
currentComposer.endNode()
}

先只看和 modifier 有关的,即 Ski前端工程师ppableUpdater&l后端工程师首要做什么t;T>(currentComposelementanimationer).skippableUpdate() 这句话,其他的先不看。

这儿后端和前端有什么差异函数的参接口的界说数都是函数参后端数类型,假定 Kotlin 不太熟悉的,或许看着比较晕 ,下面为了便利剖析一下,就把代码给它摊平了。

这儿的 “代码给它摊平了” 是指去掉函数回谐和一层层的调用appstore,直接写到一起,避免函数跳来跳去后端开发需求学什么,便后端言语当阐明。下文中说到的摊平都是这个意思。

下面我就试着把它摊平看看。

//源代码
Skippabl前端技能eUpdater<T>(cur前端结构rentComposer).skippableUpdat后端不支持corse()
//0️⃣ 依据 ComposeNode 传入参数知
skippableUpdate=materializerOf(modifier) //    modifAPPier 
//1️⃣ materia后端言语lizerOf 函数回来值就前端结构是函数类型 Skippa前端面试题bleUpdater<ComposeUiNode>.() -> Unit
internal fun materializerOf(
m后端工程师首要做什么odifier: Modifielement滑板er
): @Composable Skipp接口文档ableUpd前端结构ater<ComposeUiNode>.() -> Unit = {
//   这儿仅仅对 modifier链中存在的 ComposedModifier 进行处理一下,回来值仍是 Modifier
val materializeapp是什么意思d = cuelementaryrrentComposer.前端开发materi后端开发a前端学什么lize(modifier)
up后端date { set(materialized, ComposeUiNode.SetModifier)  } //    modifier
}
//结合代码 0️⃣ 和前端技能代码 1️⃣ 可知 SkippableUpdat后端开发需求学什么er<Telement什么意思>(currentCompoapp是什么意思ser).skippableUpdate() 
//<=> 等价于代码如下
val skippableUpdater=SkippableUpdater<Coelementary是什么意思mposeUiNode>(currentComposer)
val materialized = currentelement滑板Composer.materialize(modifier)
skippableUpdater.update { set(materialized, CompoElementseUiNode.SetModifier)  }

如同还差点还有个update 仍是没有摊平

inline class SkippableU前端和后端的差异pdater<T> constructor(element结构
@PublishedApi int后端开发是干什么的ernal val composer: Composer
) {
inline fun update(bloapplicationck: Updater<T>.() -> Unit) {
composer.startReplaceableGroup(0x1e65194f)
Updater<T>APP(composer).block()
composer.endReplaceableGroup()
}
}
//结合SkippableUpdater 的update 函数,skippableUpdater.update { set(materialized, ComposeUiNode.SetModifier)接口测验面试题 }
// <=>等后端工程师首要做什么价于
compos接口测验er.startReplaceableGroup(0x1e65194f)
// 2️⃣  modifier 究竟appetite传给了这个 set 办法
Updater<ComposeUiNode>(currentComposer).selementuiet(materialized, ComposeUiNode.SetModifier)
composer.endReplaceableGroup()

代码2️⃣ 这儿是调用了一个 set 办法,直接看有点晕,调来调用去,剖析起来太多了,不能跑偏了,直接说要点。

 companion object {
val Constructor: () -> ComposeUiNode = LayoutNoelementuide.Constructor
//ComposeUappearanceiNode.SetModifier
val SetModifier: C后端开发需求学什么omposapplicatione前端工程师UiNode.(Modifier) -> Unit = { this.modifier = it }
}
// ComposeUiNode.SetModifier 也是个函数类型,接口调用 set(materialized, ComposeUiNode.SetModifier) 
//究竟会触发SetModifier 函数的实elements是什么牌子行也便是
this.modElementifier=materialized // modifier
//	 this是LayoutNode 政策 是经过触发 ComposeUiNode.Construct后端工程师首要做什么or创立的

关于从set(materialized, ComposeUiNode.SetModifier) 是怎样到触发 SetModifier 函数的,这儿我就剖析了,能够经过element滑板 debug 很简略验证这一定论。假定你真的想去剖析怎样实施的话,剖析之前主张先看一下 深化详解 Jetpack Compose | 完毕原理 这篇文章。(友谊提示,怎样真要剖析这段别陷进去了,别忘记咱们看源码的意图。)

经过上面的剖析,咱们寻找的 modifier 被赋值给了 LayoutNode 成员的 modifier ,这种是个赋值语句,在 kotlin 相当于调用的成员变量的set接口 办法
La接口测验面试题youtNode.kt

  override var modifier: Modifier = Modifier
set(value)elementary是什么意思 {
// …… code
field = value
// …… code
// 创后端开发是干什么的建新的 LayoutNodeWrappers 链
// foldOut 相当于遍历 modifier
val outerWrapper = modif后端开发薪酬一般多少ier.foldOut(innerLayoutNodeWrapper) { mod /后端开发* modifier*/ , toWrap -&applegt;
var wrapper = toWrap
if (mod is OnGloballyPositionedModifier) {
onPositionedCallbacks += mod
}
if (mod is RemeasurementModifier) {
mod.onRe后端不支持corsmeasurementAvailable(this)
}
val delegatelements是什么牌子e = rappstoreeuseLayoutNodeWrapper(mod, toWrap)
if (delegate != null) {
wrapper = delegate
} else {
// …… 省掉了一些 Modifier判别 
if (mod is KeyInputModifier) {
wrappappetiteer = ModelementaryifiedKeyInputNode(wrapper, mod).appreciateassignChained(toWrap)
}
if (mod is PointerInputModifier) {
wrapper = PointerInputDelapplicationegatingWrapper(wrapper, mod).assignChained(toWrap)
}
if (mod is NestedScrollModifier) {
wrapper = NestedScrollDelegatingWrapper(wrapper, mod接口).assignChained(toWrap)
}
// 布局相关后端工程师首要做什么的 Modifier
if (mod is Layouelementary是什么意思tModifier) {
wrapper = ModifiedLayoutNode(wrapper, mod).assignChained(toWrap)
}
if (接口卡mod is ParentDatappstoreaModifier) {
wrapper = ModifiedParentDataNodeappetite(wrapper,appointment mod).assignChained(toWrap)
}
}
wrapper
}
outerWrapper.wrappedBy = parent?.innerLayoutNodeWrapper
// 代码 0️⃣
outerMeasurablePlaceable.outerWrapper = o接口是什么uterWrapper //    modifier
……
}

代码片段-1

上述代码首要是将Modifier 链转化LayoutNodeWrapper 链的进程,经过Modifier 的 foldOut 函数 遍历Modifier 链上的悉数元素,并依据不同的M前端开发是干什么的odifier 创立不同的 LayoutNodeWrapper。关于Modifier 的foldOut 函数的效果不懂的能够看我之前写的 Modifie接口自动化r源码,Kotlin高阶函数用的真6 这篇文章。

在上面的代码中依据 Modifier 类型创立不同的 LayoutNodeWrapper,这些不同的 Mod接口的界说i接口卡fier 都是 Modifier.Eleelements是什么牌子ment 的直接完毕类或接口,如 KeyInputModapproachifier、PointerInputModifier、LayoutModifier 等。上面代码都是 if 判前端和后端哪个薪酬高别,没有else,也前端开发是干什么的便是说假定 Modifier 不在这些类别规划内就无法创立对应的LayoutNodeWrapper,也就相等于咱们设置的 Modifier 没有用。所以我自界说Modifer后端和前端有什么差异 必定要在这个类型规划内,否则是没有用的。在JeapproachtPack Compose 内置的后端开发是干什么的Modifier.Element 子类前端结构或接口如下。(Tips.appreciate An后端工程师首要做什么droid studioelements 查看类的继承联络 菜单栏Navgate-> Type Hierarchy ; 快捷键 Ctrl+H )

Jetpack Compose 丈量流程源码剖析

换个思路持续跟踪

上面剖析到那里路如同断了, 无法持续了。考虑一下这儿仅仅剖析了Layout 函数实施时,仅仅初始化的准备工作。它的巨细和方位假定招认等操作这儿如同没有实施。咱们刚才是把 ParentL接口测验面试题ayout 作为父容器来看待的,父容器一般是办理自己的 chapp是什么意思ildren 的巨细和方位,换一种思路接口类型,ParentLayout 出来做父容器,它也能够作为 chi后端开发ld 呀,如下面代码状况。

setContent {
ParentLayout{
Box() {}
//  能够看做是 上面 ParentLayout 的 child,也能够看做是下面 ChildLayout 的父容器
ParentLayout(
Modifier
.size(100.dp)
.paappreciatedding(10.dp)
.background(Coappreciatelor.Blue)
) {
ChildLayout(Modifier.size(100.dp)) {}
}
}
}

下面就从 ParentLayout 布appear局作为 child 的时分来剖析一下,假定作为 child 那么剖析进口就应该它从的父容器 Meelement什么意思asurePolicy 的 measure 函数开始剖析了。

    val measurePolicy = MeasurePol前端和后端的差异icy { measurables, constraints ->
val placeables = measurables接口的界说.map { child ->
//代码 0️⃣    
child.measure(constraints)
}
……
}

代码 0️⃣ 进行调用 child 的丈量办法,从函数参数来看,只知道 child 是个 Measurable 的类型,但 Measurable 是个接口,咱们需求知道 ch接口测验ild 具体是 Measurable 那个完毕类,咱们才好剖析 measure 函数的逻辑
Jetpack Compose 丈量流程源码剖析 图片-0

经过deelementsbug 的办法,能够看出 child 是 LayoutNode 政策(为什么elementui是LayoutNode 下面剖析就知道了),那么就去看看 LayoutNode 的mappearanceeasure函数。

LayoutNode.kt → measure 函数

 override fuapplicationn measure(constrainelement结构ts: Constraints) =
outerMeasurablePlaceable.measure(constraiapproachnts)

LayoutNode 的 measure 调用了 outerMeasurablePlace前端面试题able 的 measure 函数,这个 outerMeasurableelementsPlaceable **代码片段-1 代码 0️⃣ **也呈现了outerMeasurablePlaceable.outerWrapper = outer前端技能Wrapper // modifier 并且这个outerMeasurablePlaceable 的特色 outerWrapper接口测验包括 modifier 信息。咱们又找后端和前端有什么差异到了 modifier 的藏身之处,如同又找到些头接口和抽象类的差异绪。咱们持续跟踪代码吧。

LayoutNodeWrap后端不支持corsper 链中的丈量流程剖析⛓

OuterMeasurablePlaceable.kt

 over前端面试题ride fun measure(constraints: Constraints): Placeable {
……
remeasure(constraints)
return this
}
fun remeasure(conselement滑板traints: Constraint后端开发是干什么的s): Boolean {
vaElementl owner = layoutNode.requireOwner()
……
if (layoutNode.layoutState == LayoutState.NeedsRemeasure ||
measurementConstrainElementts != constraints
) {
measuredOnce = true
layoutNode.layoutState = LayoutState.Measuring
measurementConstraints = constraints
val outerWrapperPreviousMeasuredSize = outerWrapper.size
owner.snapshotObserver.observeMeasureSnapshotReads(layoutNode) {
outerWrapper.measure(constraints)//  0️⃣    modifappearier
}
layoutNode.layoutState = LayoutState.NeedsRelayout
……
return sizeChanged
}
return false
}

代码片段-2
代码 0️⃣ 处 咱们看到包括 modifier 信息的 ou后端组terWrapper 调用了 它的 measure 办法。outerWrapper 是 LayoutNodeWrapper 类型的,它便是在代码片段1 处依据不同 Modifer 创立的 LayoutNodeWrapper 链。咱们给 ParentLayout 的 Modelements翻译ifer 设置为Modifier.size(100.dp).padding(1element滑板0.dp).background(Color.Blue) 。那么对应的LayoutNodeWra后端开发pper 链如下图所示

Jetpack Compose 丈量流程源码剖析

图-1

由 图-1 知代码片段-2 处的代码outerWrapper 为 ModifiedLayoutNode 类型。
ModifiedLayoutNo后端开发需求学什么de

internal class Mod接口是什么ifiedLayoutNode(
wrapped: LayoutNodeWrapper,
modifier: LayoutModifier
) : DelegatingLayoutNodeWrappstoreapper<LayoutModifierelement滑板>(wrapped, modifier) {
override fun接口类型 measureappointment(constraint后端开发需求学什么s: Constraints): Placeable = pe接口的界说rformingMeasure(constraints) {
with(接口是什么modifier) {      modifier
measappointmentureResult = measureScope.measure(wrapped, constraints)
this@ModifiappleedLayoutNode
}
}
pro接口文档tected i前端和后端哪个薪酬高nline fun performingMeasure(constraints: Constraints, block: () -> Pla前端面试题ceable
): Placeable {
measurementConstraints = constraints
val result = block()
layer?.resize(measur后端开发是干什么的edSize)
return result
}
……
}

由 图-1 知代码此刻的 modifier 为 SizeModifier 类型
LayoutModifierelement结构.kt

interfaelements翻译ce LayoutMo前端工程师difieappler : Modifie前端开发r.Element {
fun MeasureScope.measure(
measurable: Measurable,/*下一个 LayoutNodelement结构eWrapper 节点*/
constraints: Consappearancetraints/* 来着父容接口crc过错计数器或许来着上一个节点的绑缚 */
): MeasureResult
}

SizeMo后端开发薪酬一般多少difier.kt

private class SizeModifier(
private val minWidth: Dp = Dp.Unspecified,
private val minHeight: Dp = Dp.Un前端技能specified,
private val maxWidth: Dp = Dp.Unspecified,
privat接口和抽象类的差异e val maxHeig后端开发需求学什么ht: Dp = Dp.Unspecified,
private val enforceIncoming: Boolean,
inspectorInfo: Insp前端开发是干什么的ectorInfo.() -> Unit
) : LayoutModifier, InspectorValueInfo(ielements是什么牌子nspappointmentectorInfo) {
private val D前端和后端的差异ensity.targetConstraints: Constraints
get() {/*更加咱们指定的巨细生成对应的绑缚*/}elements
overr接口自动化ide fun MeasureScope.m后端和前端有什么差异easure(
measurable: Measurable,/*下一个LayoutNodeWra接口的界说pper*/
constraints: Const接口是什么raints/* 来着父容器或许来着上一个节点的绑缚 */
): MeasureResult {
val wrapp后端和前端有什么差异edConstraints = targetConstraints.let { targetConstraints ->
if (enforceIncoming) {//application当咱们给控件指定巨细前端技能时,这个值就为true
//结合父容器或许上一个节点的绑缚 和咱们指定绑缚进行结合生成一个新的绑缚
constraints.constrain(targetConstraints)
} else {appear
……
}
}
//代appearance码 0️⃣ 进行下一个 LayoutNode前端学什么Wrapper 节点丈量
val placeable = measurable.measure(wrappedapp是什么意思Constraints)
//悉数节点丈量完,开始放置
return layout(后端是做什么的placeable.width, placeable.height) {
placeable.placeRelative(0, 0)
}
}

代码 0️⃣ 持续进行下一个 LayoutNodeWrapper 节点appetite的丈量,一向到最后 InnerPlaceableelement结构 节点。

 class LayoutNode{
internal val innerLayoutNodeWrapper: LayoutNodeWrapper = InnerPlac接口测验面试题eable(this)
private val outerMeaelement结构surablePlaceable = OuterMeasurablePlaceablelement什么意思e(this, innerLayoutNodeWrapper)
……
internal val children: List&lappreciatet;LayoutNode> get() = _children.asMutableList()
}

InnerPlaceable

class InnerPlaceable{
overrappstoreide fun measure(constraints: Constraints): Placeable = performingMeasure(constraints) {
val measureResult = with(layoutNode.measurePolicy) {
// 这儿便是Layout 的Mea前端和后端的差异su后端和前端有什么差异rePolicy 的measure 实施的当地了
layoutNode.meaAPPsureScope.measure(layoutNode.chielementaryldren, constraints)
}
layoutNode.happstoreandleMeasureResult后端开发(appearancemeasureResult)
return this
}
}

这儿便是 Layout 的 MeasurePolicy 的me后端工程师首要做什么asure 实施的当地了,然后ch接口类型ildren 持续实施上述流程了如下图所示,这样“MeasurePolicy 接口的 measure 办法是怎样调用的?他的参数值是怎样来的呢?”这个问题也就答复了。
Jetpack Compose 丈量流程源码剖析

剖析答复问题

经过上面的剖析,咱们大接口测验致能够答复“布局中的丈量流程是什么样的?” 这个问题了。**

1 准备阶段:

child 在父容器在声明时,也便是调用了 Layout 函数,进行初始化准备操作,记载这个child 测丈量战略,这个child 的children 等,依据设置的 M后端odifier 链创立对应的 LayoutNodeWrappappleer 链。

2 丈量阶段

child 在父容器的丈量战略 MeasurePolicy 的 measure 函数中实施 child 的 measure 函数。接着按照准备好的 LayoutNodeWrapper 链一步接口crc过错计数步的实施各个节点的 measure 函数,elementanimation究竟走到 InnerPlaceable 的 measure 函数,在这个又会持续它的 children 进行丈量,此刻它的 children 就会和它相同进行实施上述流程,一向到悉数childre前端n 丈量完毕。
用下面这张图总结一下上述流程。后端开发需求学什么

Jetpack Compose 丈量流程源码剖析

图-2 丈量流程图

接口和抽象类的差异有一个最后一个问题
ParentLayout 中后端言语 经过 modifier 设置巨细是怎样起到效果的 ?
答:接口测验面试题咱们经过 Modifer.size() 函数 构建了一个SizeMo接口自动化difer 政策

fun Modifier.size(size: Dp) = this.then(
SizeModifier(
minWidelementary是什么意思th = size,maxWidth = size,
minHe接口测验igh前端开发是干什么的t = size, maxHeight = size,
enforceIncoming = true,...
)
)

经过上面的剖析咱们知道,在丈量流程中S接口和抽象类的差异izeM前端结构odifer 的meas前端工程师ure 函数会触发
Jetpack Compose 丈量流程源码剖析

SizeModifer部后端是做什么的分源码

private class SizeMoelement滑板difier(
private val minWidth: Dp = Dp.Unspecifelements翻译ied,
private val minHeight: Dapplicationp = Dp.Unspecified,
priapp是什么意思vate val maxWidth: Dp = Dp.Unappearancespecified,
private val maxHeight: Dp = D接口卡p.Unspecified,
private val enforceIncoming: Boolean,
inspectorInfo: InspectorInfo.() -> Un接口卡it
) : LayoutModifier, InspectorValueIn接口fo(inspectorInfo) {
// 0️⃣ 依据咱们设置的巨细生成一个绑缚政策
private val Density.targetConstraints: Constraints
get() {
//操控 maxWidth值规划
val maxWidth = if (maxWidth != Dp.Unspecified) {
maxWidth.coerceAtLeast(0.dp).ro接口crc过错计数undToPx()
} else {
Constraints.Infinity
}
//ma接口是什么xHeight接口自动化、minWidth、minHeight 也类似处理一下值前端和后端的差异的规划,避免咱们瞎捣乱
...code...
return C前端开发需求学什么onstraints(
minWappetiteidth = minWidth,
minHeight = minHeight,
maxWidth = maxWidth,
maxHeight = maxHeight
)
}
overri后端开发de fun MeasureScope.measure(
measurable: Measurable,//LayoutNodeWrapper链上的下一个节点的
constraints: Constraint接口和抽象类的差异s//父容器或许是LayoutNodeWrapper链上的上一个节点的constra后端ints
): MeasureResult {
val wrappedConstraints = tar前端面试题getConstraints.let { targetConstraints ->
if (enforceIncomin后端开发需求学什么g) {
//1️⃣  和咱们设置的巨细持续比较核算,得出一个新的Constraints政策
// constrain 函数效果是elements是什么牌子,在constraints规划内,得到一个尽或许满足targetConstraints的巨细规划的绑缚
constraints.constrain(targetConstraints)
} else {
//暂不评论
}
/appointment/2️⃣  运用新的绑缚持续下一个丈量
val placeable = measurable.measure(wrappedConstraints)
return layout(placeable.width,接口的界说 placeable前端工程师.height) {
placeable.placeRelative(0, 0)Element
}
}
}
...
}

咱们经过Modifier.size 函数设置的巨细信息究竟变成SizeModifier 一个成员变量 targetConstraints,它是一个Constraints 类型,首elementui要描绘宽度的最小值和最大值(minWidth、maxWidth) 以及高度的最小值最大值(minHeight、maxHeight)。
在 measure 函数中,在代码 1️⃣ 处把传入的 constraints 和咱们传入的巨细进行巨细比较得到前端工程师一个适宜的 Constraints ,然后后端组用新的 Constraelements是什么牌子ints 进行下一个次丈量。咱们elements翻译设置的巨细便是在这儿起到效果的,影响着接下来的丈量绑缚参数。

补充点-布局宽高核算流程

上面咱们把measure 丈量流程走了一遍,丈量的意图便是为了布局的宽高核算,那这些核算的进程是在哪里实施的呢?咱们经过 Jetpack Compose 写界面,咱们的写后端工程师首要做什么每一个组件都会转化成对应的节点 LayoutNoe,在Layout 函数中就有一个创立Layou接口的界说tNode的进程。咱们想剖析局的究竟宽高,就看 LayoutNode 的宽高怎样来的就行了。

class LayoutNode{
pElementrivate valappearance outerMeasurelement滑板ablePlaceable = OuterMeasurab前端lePlaceable(this, inelementanimationnerLayoutNodeWrapper)
override val welement滑板idth: Int get() = outerMeasurablePlaceable.width
override val height: Int get() = outerMeasurablePlaceable.height
}

看LayoutNode 源码可知,LayoutNode 的宽高便是 OuterMeasurablePlaceable 的宽高,咱们找到 OuterMeasurablePlaceable 的宽高招认就能够了,上面我现已把丈量流程大致剖析后端开发需求学什么了一下,结合 图-2 丈量流程图 再把流程走一下就能够找到后端开发答案了,这儿我就不在一段一段代码的去剖析了,elementanimation我把 OuterMeaelement结构surablePlaceable 的宽高核elements是什么牌子算代码流程绘制成下面图片了,有喜好的能够结合图片去看源码剖析一遍。

Jetpack Compose 丈量流程源码剖析

图-3 节点宽高核算代码流程图

大致流程是,在父容器的MapproacheasurePolicy 进行 child 的丈量->进入OuterMeasurablePlaceable的丈量函数,在这儿先依据函数传入的Constraints绑缚核算一下宽高前端学什么,然后沿着LayoutNodeWrapper 链去丈量,链的每一个节点依据上一个节点传来的绑缚进行一次宽高核算,一向到最接口后的InnerPlaceable的丈量,它同样是依据传入的绑缚核算宽高,然后会丈量 children,丈量完所以的 chelement结构lidren之后得到一个丈量效果,依据这个丈量效果,InnerPlaceable 再一次进行核算宽高。然后把究竟的宽高信后端和前端有什么差异息回来给上一个节点后端言语,上一个节点依据回来的丈量效果信息从头核算宽高,沿着 LayoutNodeWrapper 链反向的回来丈量效果,每个节点从头核算后再把效果回来给上一级,一向到 OuterMeasurablePlaceable,它再拿到丈量效果从头核算宽高 OuterMeasurablePlaceable 的宽高便是 LayouappeartNode 的宽高,整个宽高招认的流程大致便是这样了。

Jetpack Compose 丈量流程源码剖析

节点宽高简易流appear程图

每一个节点都会依据Constraints 绑缚核算记一次宽高,一起也把这个绑缚条件记载了下来,然后依据丈量的效果再一次核算宽高,仔细看核算宽高前端面试题的逻辑你就会发现,大致的原因是这样的,先是按照绑缚的最小值核算宽高,比如绑缚是 Constraints(接口minWidth = 80, maxWidth = 120, minHeight = 90, maxapp是什么意思Height = 150) 那么此刻宽高值分别是80,90。经过丈量回来宽高效果是100,120,拿这个值和本来记载的绑缚后端组进行比较,发element什么意思现宽高的值都在对应的绑缚规划内,那究竟的宽高值就elementanimation以丈量效果的宽高值为准,假定丈量效果不在绑缚apple规划内,那宽高就取绑缚条件中对应的最小值或最大值。记载后端是做什么的绑缚条件的意图便是避免丈量效果超越此约束。

总结回想

本文经过寻找Layout 源码,经过剖析答复了一些问题,下面简略再回想一下。

1.布局中经过 modifiapplicationer 设置巨细是怎样起到效果的 ?

答:是在 ModifiedLayoutNode 的丈量函数中调用 LayoutModifier 的丈量函数中起到效果的,比如 SizeModifer 的 measure 函数。具elementui体剖析见上文。

2.MeasurePo后端工程师首要做什么licy 接口的 measure 办法是怎样调用的?

答:是在 Ielement什么意思nnerPlaceable 的measur接口的界说e 函数中调用的。具体剖析见上文。

3.布局中的丈量流程是后端开发是干什么的什么样的及宽高的招认?

答:见图-2 丈量流程图和图-3 节点宽高核算代码流程图。具体剖析见上文。

发表评论

提供最优质的资源集合

立即查看 了解详情