这是我参加更文应战的第7天,活动详情查看: 更文应战
现在有一个正在进行的 Jetpack Compose中文手册 项目,旨在帮助开发者更好的了解和掌握android平板电脑价格Compose结构,现在仍还在开荒中,欢迎咱们重视与参加! 本文现已收录到该手册中,欢迎查阅
compose.runtime
Jetpackgithub怎样下载文件 Compose 不只是一个 UI 结构,更是一个通用的 NodeTree 处理引擎。本文介绍
compose.runtime怎样经过NodeTree为compose.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组件 |
其间 compose.runtime 和 compose.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 的创立与更新。
如上,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 处理触及 Applier、Composition 和 Compose Nodes 的作业:
Composition 作为起点,建议初度的 composition,经过 CoCanvasmposalbe 的实施,填充 Slot Table,并根据 Table 创立 NodeTree。烘托引擎根据 Compose Nodes 烘托 UI, 每逢 recomposition 产生时,都会经过android下载装置 Applier 对 NodeTree 进行更新。 因此canvassing
“Composable 的实施进程便是创立 Node 并构建 NodeTree 的进程。 ”
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()
}
insertTopDown 与 insertBottomUp 都用来增加节点,针对不同的树形结构挑选不同的增加次序有助于进步功用。 参算法的时刻复杂度是指什么看: insermaterial design风格tTopDown
| insertTopDown(自顶向下) | inCanvassertBottomUp(自底向上) |
|---|---|
![]() |
![]() |
咱们能够完毕自定义的 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政策,参数传入 Applier 和 Recoandroidstudio装置教程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<github敞开私库;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<android/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 等操作。 此处的 startNode,useNode,endNode 等便是对 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
}
效果展示:
TL;DR
- 当 State 改动时触发 recomposition,Composable 从头实施算法的时刻复杂度取决于
- Composable 在实施中,经过 SlotTable 的 diff,找出待改动的 Node
- 经过 Applier 更新 Treandroid/yunoseNmaterial design是什么ode,并在 UI 层烘托这棵树。
- 根据 compose.ru算法的特征ntime ,咱们能够完毕自material design 教程己的声明式UI








评论(0)