这是我参加更文应战的第7天,活动详情查看: 更文应战

现在有一个正在进行的 Jetpack Compose中文手册 项目,旨在帮助开发者更好的了解和掌握android平板电脑价格Compose结构,现在仍还在开荒中,欢迎咱们重视与参加! 本文现已收录到该手册中,欢迎查阅

compose.runtime

Jetpackgithub怎样下载文件 Compose 不只是一个 UI 结构,更是一个通用的 NodeTree 处理引擎。本文介绍 compose.runtime 怎样经过 NodeTreecompose.ui 供给支撑。

咱们知道 Jetpack Compose 不只限于在 Android 中运用 ,Compose For Desktop、 Compose For Web 等项目也已相继发布,未来也许还会出现 Compose For iOS 。Comgithub直播渠道永久回家pose 能够在不同github下载渠道上完毕相似的声明式UI开发体会,这得益于其分层的规划。

Compose 在代码上自下而上依次分为6层:

Modules Description
compose.compiler 根据 Kotlin compiler plugin 对 @material design规划标准Composable 进行编译期代算法是什么码生成和优化
co算法的时刻复杂度取决于mpose.runtime 供给 NodeTree处理、State处理等,声明式UI的根底运行时
compose算法导论.ui Android设备相关的根底UI才华,例如 layout、measure、drawing、input 等
compose.foundacanvas动画tion 通用的UI组件,包括 Column、Row 等容Android器、以及各种 Shape 等
compandroid是什么手机牌子ose.animation 担任动画的完毕、进步用户体会
compose.materialgithub直播渠道永久回家 供给契合 Material DMaterial+Designesign算法剖析的意图是 标准的UI组件

Jetpack Compose Runtime : 声明式 UI 的根底

其间 compose.runtimecompose.compiler 最为中心,它们material design标准是支撑声明式UI的根底。

Jake Wharton 在他的博客github敞开私库说到 :

What this means is that Compose is, at its core, a general-androidstudio装置教程purpose tool for managing a tree of nodes of any type. Well a “tree of nodes” describes just about aandroid是什么手机牌子nything, and as a result Compose can target just about anything.

– jak算法导论ewhartoncanvas下载.com/a-jetpack-c…

compose.runtime 供给了 NodeTree 处理等根底才华,此部分与渠道无关,在此根底上各渠道只需完毕UI的烘托便是一套无缺的声明式UI结构。而 compgithub怎样下载文件ose.compiler 经过编译期的优化,帮助开发者书写更简略的代码调用 runtime 的才华。

从 Composable 到 NodeTree

“Compose 也好,React、Flutter 也好,其代码本质上都是对一颗树形结构的描绘。”

所谓“数据建议UI”,便是当state改动时,重建这颗树型结android下载装置构并根据这棵NodeTree改写UI。 当然,处于功用考虑,当 NodeTree 需求重建时,各结构会运用 VirtualDom 、GapBuffer(或称SlotTable) 等不同技术对其进行“差量”更新,避免“全量”重建。compose.runtime 的重要作业之一便是担任 NodeTree 的创立与更新。

Jetpack Compose Runtime : 声明式 UI 的根底

如上,React 根据 VDOM “差量”canvas 更新右侧的DOM树。

Compose 中的 NodeTree

关于 OOP 言语,咱们一般运用如下方法描绘一颗树:

fun TodoApp(items: List<TodoItem>): Node {
return Stack(Orientation.Vertical).apply {
foandroidstudio装置教程r (item in items) {
children.add(Stack(Orientation.Horizontal).apply {
children.add(Text(if (item.completed) "x" else " "))
children.add(Tematerial design标准xt(item.title))
})
}
}
}

TodoApp 返回 Node 政策,能够被父 Node 继续 add,循环往复构成一棵无缺的树。

但是 OOP 的写法模板代码多,不行简练,且缺少安全性。返回值 Node 成为句柄被随意引用甚至修改,这破坏了声明式UI中 “不行变性” 的原则,假如 UI 能够随意修改,diff 算法的准确性material design 教程将无法确保。

因此,算法为了确保 UI 的不行变性,咱们设法抹去返回值 Node:

fun Composer.TodoApp(items: Lmaterial design标准ist<TodoItem>) {
Stack(Orientation.Vertigithub怎样下载文件cal) {
for (item in items) {
Stamaterialdesign下载ck(Orientation.Hcanvas交大orimaterial design是什么zontal) {
Text(igithub是干什么的f (itemMaterial+Design.completed) "x" else " ")
Text(item.title)
}
}
}
}
fun Composer.Stack(orientation:Int, content: Composer.()material design风格 -> Unit) {
emit(StackNode(orientation)) {
content()
}
}
fun Composer.Tecanvasxt() {
...
}

经过 Composer 供给的上下文, 将创立的 Node em算法it 到树上的适合方位。

interface Composer {
// add node as a child to the current Node, execute
// `content` with `node` as the curgithub官网rent Node
fun emit(node: Nodeandroid手机, content: () -> Unit = {})
}

Composer.Stack()material design规划标准 作为一个无返回值的函数,使得 NodeTree 的构建从 OOP 方法变为了 FP(函数式编程) 方法。

Compose Compiler 的加持

compose.compiler 的意义是让 FP 的写法进一步简略,增加一个 @Composable 注解, TodoApp 不必定义成 Composer 的扩展函数material design风格, 但是在编译期会修改 TodoApp 的签名,增加 Composer 参数。

@Composable
fun TodoApp {
Stack {
for (item in items) {
Stack(Orientation.Horizontal){
Text(if (item.completed) "x" else "算法的时刻复杂度是指什么 ")
Textcanvas动画(iteandroidstudio装置教程m.tandroid平板电脑价格itle))
})
}
}
}

在 Compiler 的加持下,咱们能够运算法是什么用 @Composable 高效地写代码。 抛开言语上的差异不讲,Compose 比 Flutter 写起来要舒畅得多。但不论写法上有多少不同,其归根结度仍是会转换为对 NodeTrAndroidee 的操作

NodeTree算法的时刻复杂度取决于操作:Applier、ComposeNodecanvas翻译、Composition

Comp算法是什么ose 的 NodeTree 处理触及 ApplierCompositionCompose Nodes 的作业:

Composition 作为起点,建议初度的 composition,经过 CoCanvasmposalbe 的实施,填充 Slot Table,并根据 Table 创立 NodeTree。烘托引擎根据 Compose Nodes 烘托 UI, 每逢 recomposition 产生时,都会经过android下载装置 Applier 对 NodeTree 进行更新。 因此canvassing

“Composable 的实施进程便是创立 Node 并构建 NodeTree 的进程。 ”

Jetpack Compose Runtime : 声明式 UI 的根底

Applier:改动 NodeTree 的节点

android的drawable类文说到,算法导论出于功用考虑,NodeTree 会运用 “差量” 方法自我更新,而这正是根据 Applimaterial design规划标准er 完毕的。 Applier 运用 Visitor 方式遍历树上的 Node ,每种 NodeTree 的运算canvassing都需求配套一个 Applier

Applier 供给material design什么意思回调,根据回调咱们能够对 NodeTree 进行自定义修改:android是什么手机牌子

interface Applier<N> {
val current: N // 其时处理的节点
fun onBeginChanges() {}
fun onEndChanges() {}
fun down(node: N)
fun up()
fun insertTopDown(index: Int, instance: N) // 增加节点(自顶向下)
fun insertBottomUp(算法剖析的意图是index: Int, instance: N)// 增加节点(自底向上)
fun remove(index: Int, count: Int) //删去节点
fun move(from: Int, to: Int, count: Int) // 移动节点
fun clear()
}

insertTopDowninsertBottomUp 都用来增加节点,针对不同的树形结构挑选不同的增加次序有助于进步功用。 参算法的时刻复杂度是指什么看: insermaterial design风格tTopDown

insertTopDown(自顶向下) inCanvassertBottomUp(自底向上)
Jetpack Compose Runtime : 声明式 UI 的根底 Jetpack Compose Runtime : 声明式 UI 的根底

咱们能够完毕自定义的 NodeApplier, 如下:

class Node {
val children = mutableLmaterial design规划标准is算法的时刻复杂度取决于tOf<Node>()
}
claandroid是什么手机牌子ss NodeApplier(node: Node) : AbstractApplier<Node>(canvas动画node) {
override fun onClear() {}
override fun insertBottomUp(index: Int, instance: Node) {}
override fun insertTopDown(index:GitHub Int, instance: Node) {
current.children.add(index, instangithub是干什么的ce) // `current` is set to the `Node` that we wancanvas交大t to modify.
}
override fun move(from: Int,Android to: Int, count: Int) {
current.children.move(from, to, count)
}
override fun remove(index: Int, count: Int) {
currentandroid下载装置.children.remove(index, candroid手机ount)
}
}

Applier 需求在 composition/recomposition 进程中被调用。compoCanvassition 是经过 Composition 中对 Root Composable 的调用建议的,从github永久回家地址而调用悉数 Composalbe 毕material design是什么竟构成NodeTree。

Composition:Composalbe 实施的起点

fun Composition(applier: Applier<*>, parent: CompositionContext) 创立 Composition政策,参数传入 ApplierRecoandroidstudio装置教程mposer

val composition = Composition(
applier = NodeApplier(node = Node()),
parent = Recomposematerial design规划标准r(Dispatchers.Main)
)
composition.setContent {
// Composable function cacanvas翻译lls
}

Recomposer 非常重要,他担任 Compose 的 recocanvassingmposiiton 。当 NodeTree 初度创立之后,与 state 树立相关,监听 state 的改动产生重组。这个相关的树立是经过 Recomposer 的 “快照体系” 完毕的。重组后,Recomposer 经过调material design什么意思用 Applier 完毕 NodeTree 的改动 。

canvas教育渠道于 “快照体GitHub系” 以及 Recomposer 的原理能够参看:

  • compose.net.cn/principle/s…
  • compose.net.cn/principle/r…

Composition#setContent 为后续 Compodable 的调用供给了容器:


interface Composition {
val hasInvalidaticanvas交大ons: Boolean
val isDisposed: Boolean
fun dispose(android什么意思)
fun setContent(content: @Coandroid/yunosmposable () -> Unit)
}

ComposeNode:创立 UiNode 并进行更新

理论上每个 Composable 的实施都对应一个 Node 的创立, 但是因为 NodeTree 无需全量重建android什么意思,所以也不是每次都需求创立新 Node。大多的 Composalbe 都会调用 ComposeNode() 承受一个 factory,仅在必要的时分创立 Node。

Layout 的完毕为例,

@Composable inline fun Layout(
content: @Composable () -&g算法是什么t; Unit,
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
val density = LocalDensity.current
val layoutDiregithub中文社区ctiogithub敞开私库n = LocalLayoutDirection.currentmaterial design风格
ComposeNode&ltgithub敞开私库;ComposeUiNode, Applier<Any>>(
factory = Composandroid下载装置eandroidstudio装置教程UiNode.Constructandroid下载装置or,
upcanvasdate = {
set(measurePolicy, ComposeUiNode.SetMeasurePolicy)
set(density, Composmaterial design风格eUiNodeandroid的drawable类.SetDensity)
set(layoutDirection, ComposeUiNode.SetLayoutDirection)
},
skippableUpdamaterial design是什么te = materializerOf(m算法的有穷性是指odifier),
content = content
)
}
  • factory:创立 Node 的工厂
  • update:承受 receiveandroidstudio装置教程r 为 Updater<T>material design风格 lambda,用来更新其时 Node 的特征
  • content:调用子 Comcanvas教育渠道posable

ComposeNcanvas下载ode() 的完毕非常算法的有穷性是指简略:

inline fun <T, reified E : Applier<*>> ComposeNode(
noinline factory: () -> T,
update: @DisallowComposableCalls Updater&ltandroid/yunos;T>.() -> Unit,
noinlingithub永久回家地址e skippableUpdate: @Composable Skippablematerial design 教程Upandroid什么意思dater<T>.() -> Unit,
content: @Composable () -> Unit
) {
if (currentComposer.applgithub是干什么的ier !is E) invalidApplier()
currentComposer.startNode(Android)
if (currentComposer.inserting) {
cur算法的时刻复杂度是指什么rentComposer.createNode(factory)
} else {
currentComposer.useNode()
}
Updater<T>(curre算法ntCompomaterial design什么意思ser).update()
SkippableUpdater<T>(currentComposer).skippableUpdate()
currentComposer.startReplaceableGroup(0x7ab4aae9)//在编译期抉择真实的GroupId
conte算法导论nt()
currentComposer.endReplaceableGroup()
currentComposer.endNode()
}

在 composition 进程中,经过 Composer上下文,更canvas登录新 SlotTable, content()递归创立子 Node

SlotTable算法规划与剖析 在更新进程中,经过 diff 抉择是否需求对 Node 进行 add/update/removmaterial design官网e 等操作。 此处的 startNodeuseNodeendNode 等便是对 SlotTable 的遍历进程。

有关 SlotTable(GapBuffer) 的介绍,能够参看文章:compose.net.cn/principle/g…

Sgithub永久回家地址lotTable 的 diff 结果经过 Applier 的回调处理 NodeTree 结构的改动;经过调用 Upandroid什么意思datercanvas下载<T>.update() 来处理 Node 特征的改动

Jake whgithub是干什么的arton 的实验项目 Mosica

根据 compose.runcanvas标签timaterial design 教程me 能够完毕任意一套声明式UI结构。canvas标签 J神有一个实验性的项目 Mosica,就很好地展示了这一点 :github.com/Jacanvas交大keWharton…

fun main() = runMosaic {
var count by mutableStateOf(0)
setContent {
Text("The count is: $count")
}
for (i in 1..20) {
delay(250)
count = i
}
}

上面是 Mosica 中的一个github中文官网网页 Counter 的比如。

Mosica Composit算法的五个特性ion

runMosaic() 创立 Compocanvassingsitionmaterial design规划标准、Recomposer 和 Applier

fun runMosaic(body: suspend MosaicSmaterial design 教程cope.() -> Unigithub敞开私库tgithub永久回家地址) = runBlocking {
//...
val job = Job(coroutineContext[Job])
val compos算法的有穷性是指eContext = coroandroid/yunosutineContext + clock + job
val rootNode = BoxNomaterial design官网de()Android //根节点Node
val recomposer = Recomposer(composeContext) //Recomposer
val composition = Composition(MosaicNodeApplier(rootNode), recomposer) //Composition
coroutineScope {
val scope = object : MosaicScope, CoroutineScope by thigithub中文社区s {
override fun setContent(content: @Composable ()android手机 -> Unit) {
composition.setContent(content)//调用@Composable
hasFrameandroid下载装置Waiters = trugithub直播渠道永久回家e
}
}
//...
val snapshotObserverHandle = Snapshot.rGitHubegisterGlobalWriteObserver(observer)
try {
scope.body()//CoAndroidroutineScope中实施setContencanvas登录t{}
} finally {
snapshotObserverHandle.dispose()
}
}
}

然后,在 Composition 的 setContegithub打不开nt{} 中,调用material design规划标准 @Composable。

Mosaic Node

看一下 Mosaic 中的 @Composalbe 和其对应的 Node

@Composable
private fun Box(flexDirection: YogaFlexDirection, children: @Composable () -> Unit) {
ComposeNode<BoxNode, Mosaic算法的时刻复杂度是指什么NodeApplier>(
factory = ::BoxNode,
update = {
set(flexDirection) {
yoga.flexDirection = flexDirection
}
},
content = children,
)
}
@Composable
fun Text(
value: String,
color: Color? = null,
background: Color? = null,
style: TextStyle? = null,
) {
CoAndroidmgithub中文官网网页poseNode<Texgithub官网tNode, MosaicNodeApplier>(::TextNode) {
set(value) {
this.material design风格value = value
}
set(color) {
this.for算法规划与剖析eground = color
}
set(background) {
this.background = background
}
set(style) {
this.style = style
}
}
}

ComposeNode 经过泛型相关对应的 Noandroid手机de 和 Applier 类型

Box 和 Text 内部都运用 ComposeNode() 创立对应算法的时刻复杂度取决于的 Node 政策。其间 Box 是容器类的 C算法的时刻复杂度取决于omposalbe,在 conent 中进一步创立子 Node。 Box 和 Text 在Updacanvas交大ter<T>.update()github敞开私库更新 Node 特征 。

看一下 BoxNode:

internamaterial design官网l class BoxNode : MosaicNode() {
val children = mutableListOf<MosaicNode>()
override fun renderTo(canvas: TextCanvas) {
for (child in children) {
val childYogagithub永久回家地址 = child.yoga
val left = chilGitHubdYoga.canvas标签layoutX.toInt()
val top =算法的时刻复杂度是指什么 childYoga.layoutY.toInt()
val right = left + childYoga.layoucanvas下载tWidth.toInt() - 1
val bottom = top + childYoga.layoutHeight.toInt() - 1
child.renderTo(github下载canvas[top..bottom, left..right])
}
}
override fun toString() = chandroid平板电脑价格ildren.joinToString(prefix = "Box(", postfix = ")github永久回家地址")
}
imaterial design怎样读nternal sealed class MosaicNode {
valcanvas交大 yoga: YogaNode = YogaNodecanvasFac算法的时刻复杂度是指什么tory.crea算法剖析的意图是te()
abstract fun rendecanvasrTo(canvas: TextCan算法的时刻复杂度是指什么vas)
fun regithub打不开nder(): String {
val canvas = with(yoga) {
calculateLayoutandroidstudio装置教程(UNDEFINED, UNDEFINED)
T算法规划与剖析extSurface(layoutWidcanvas动画th.toInt(), layoutHeight.toInt())
}
renderTo(canvas)
return canvas.toString()
}
}

Boxgithub直播渠道永久回家Node 继承自 MosaicNode, MosaicNode 在 render() 中,经过 yoga 完毕UI的制作。经过调github是干什么的renderTo() 在 Canandroid手机vas中 递归制作子 Node,相似 AndroidView 的制作逻辑。

理论android/yunos上需求在初度 composition 或许 recomposition 时,调用 Node 的 render() 进行 NodeTree 的制作, 为简略起见,Mosica 只是运用了定时轮询的方法调用 render()

	launch(contexMaterial+Designt = composeContext) {
whilematerial design是什么 (true) {
if (hasFrameWaiters) {
hasFrameWaiters = false
output.dis算法剖析的意图是play(roocanvas教育渠道tNode.render())
}
delay(50)
}
}
//counter的state改动后,算法剖析的意图是从头setContenmaterial design是什么t,hasFrameWaiters更新后,从头render
coroutineScopcanvassinge {
val scope = object : MosaicScope, CoroutineScope by this {
override fun setContent(content: @Composable () -> Unit) {
composition.setContent(android手机content)
hascanvas标签FrameWaiters = true
}
}
}

Mosaicandroid什么意思NodeApplier

最终看一下 MosaicNodeApplier:

internal class MosaicNodeAcanvassingpplier(root: BoxNode) : Abstcanvas标签ractApplier<MosaicNode>(root) {
override fun insertTopDown(index: Int, instanandroid是什么手机牌子ce: MosaicNode) {
// Ignored, we insergithub中文官网网页t bottom-up.
}
override fun insertBottomUp(index: Int,canvassing instance: MosaicNodgithub怎样下载文件e) {
val boxNode = current acanvass BoxNode
boxNode.children.add(index, instance)
boxNode.yoga.addChildA算法的特征t(igithub敞开私库nstance.yoga, inCanvasdex)算法的有穷性是指
}
override fun remove(index算法是什么: Int, count: Int) {
val boxNode = current as BoxNode
boxNode.chgithub打不开ildren.remove(indeandroid什么意思x, count)
repeat(count) {
boandroid/yunosxNode.yoga.removeChildAt(index)
}
}
override fun move(frandroid体系om: Int, to: Int, count: Int) {
val boxNode = currentgithub中文社区 as BoxNode
boxNode.children.move(from, to, count)
val yoga = boxNode.yoga
val newIndex = if (to > from) to - count else to
if (count == 1) {
val node = yoga.removeChildAt(from)
yoga.addChildAt(node, newIndex)
} else {
val nodes = Array(count) {
yoga.removeChildAt(froandroid是什么手机牌子m)
}
nodes.forEachIndexed { offset, node ->
yoga.addChildAt(node, newIndex + offset)
}
}
}
override fun onClear() {
val boxNomaterial design是什么de = root as BoxNode
// Remove in reverse to avoid internal list copies.
for (i in boxcanvassingNode.yoga.childCount - 1 downTo 0) {
boxNode.yoga.removeChildAt(i)
}
}
}

MosaicNodeApplier 完毕了对 Node 的 add/move/remove, 究竟都反映到了对 YogaNode 的操作上,经过 YogaNode 改写 UI

根据 AndroidView 的声明式UI

参看 Moscia 的演示,咱们能够运用 compose.runtime 打造一个根据 AndAndroidroid 原生 View 的声明式 UI 结构。

LinearLayout & TextVandroid体系iew Node

@Composable
fun TextView(
text: String,
onClick: () -> Unit = {android平板电脑价格}
) {
val context = localContext.current
ComposeNode<TextView, ViewAppli算法er>(material design期刊
factory = {
TextView(context)
},
update = {
set(text) {
this.text = text
}
set(onCgithub怎样下载文件lick) {
setOnClickListener { oandroid体系nClick() }
}
},
)
}
@Composable
fun LinearLayout(children: @Composable () -> Unit) {
val cgithub打不开ontext = localContext.current
ComposCanvaseNode<LinearLayout, ViewApplier>(github下载
factory = {
LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
laandroid/yunosyoutParamsCanvas = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATC算法工程师H_PARENT,
ViewGrmaterial design什么意思oup.LayoutParams.MATCmaterial design期刊H_PARENT,
)
}
},
update = {},
content = children,
)
}

ViewApplier

ViewApplier 中只github直播渠道永久回家完毕 add

class ViewApplier(valcanvas网页版 view: FrameLayout) : AbstractApplier<View>(view) {
override fun onClear() {
(view as? ViewGroup)?.removeAllViews()
}
override fun insertBottomUp(index: Int, ins算法的特征tance: Vcanvassingiew) {
(current as? ViewGroup)?.addView(instance, index)
}
override fun insertTopDown(index: Int, inst算法工程师ance: View)material design风格 {
}
override fun move(from: Int, to: Int, count: Int) {
// NOT Supported
TODO()
}
override fun remove(index: Int, count: Int) {
(v算法规划与剖析iew as? ViewGroup)?.GitHubremoveViews(index, count)
}
}

创立 Composition

创立 Root Composable: AndroidViewApp

@Composable
private fun AndroidViewAgithub直播渠道永久回家pp() {
var count by remember { mutableStateOf(1) }
LinearLa算法的五个特性yout {
TextView(
text = "This icanvas交大s the Android TextView!!",
)
repeat(count) {
TextView(
text = "Android View!!TextView:$it $count",
onClick = {
count++
}
)
}
}
}

最终在 content 调用 AndroidViewApp

fun runmaterial design什么意思App(context: Cogithub直播渠道永久回家ntext): FrameLayout {
val composer = Recomaterial design怎样读mposer(Dispatchers.Main)
GlobalSnapshotManager.ensureStarted()
val mainScope = MainScope()
maigithub永久回家地址nScope.lau算法nch(start = CoroutineStart.UNDISPATCHED) {
withContextmaterial design是什么(coroutineContext + DefaultMonotonicFrameClock) {
composer.runRecomposeAndApplyChanges()
}
}
maicanvas登录nScope.launch {
composer.state.collect {
println("composer:$it")
}
}
val roocanvas标签tDocument = FrameLayout(context)
Composition(ViewAppliermaterialdesign下载(rootDocument), composer).appl算法规划与剖析y {
setContent {
CompositionLocalProvider(localContext provides context) {
AndroidViewApp()
}
}
}
return rootDocument
}

效果展示:

Jetpack Compose Runtime : 声明式 UI 的根底

TL;DR

Jetpack Compose Runtime : 声明式 UI 的根底

  • 当 State 改动时触发 recomposition,Composable 从头实施算法的时刻复杂度取决于
  • Composable 在实施中,经过 SlotTable 的 diff,找出待改动的 Node
  • 经过 Applier 更新 Treandroid/yunoseNmaterial design是什么ode,并在 UI 层烘托这棵树。
  • 根据 compose.ru算法的特征ntime ,咱们能够完毕自material design 教程己的声明式UI