Compose UI

使用Compose写UI

compose是android新推出的UI工具包,使用可组合函数以声明式来构建UI,不再使用xml布局文件

使用compose构建ui的例子:

image.png

运行效果图


image.png

既然Compose是全新的一套UI工具包,那么它是如何构建UI的,是否用到了诸如LinearLayout、RelativeLayout、Button、Text这些android view呢?

让我们来分析上图的布局

遍历view,输入如下:

image.png

没有ImageView、TextView等android view,看来compose是完全自己实现了一套布局和绘制流程。

虽说compose没有使用android的ui组件,但是如果没有硬件加速,还是会有很多子view,例如关闭compose的一个标志位,会出现很多view。如反射设置androidx.compose.ui.platform.AndroidComposeView.isRenderNodeCompatible为false,再打印view树如图:

image.png

多出了ViewLayerContainer,它包含了很多ViewLayer。后面再分析为什么会出现这么多ViewLayer。

可重组方法执行流程

Column方法,最后执行Layout方法

image.png

Image方法,最后执行Layout方法

image.png

Text方法一样最后执行Layout方法

Layout方法

image.png

执行ComposeUiNode.Constructor会创建一个LayoutNode

image.png
image.png

最后所有的方法都执行到了ComposeNode方法,这里边进行创建LayoutNode并创建子LayoutNode。基本上可以确定compose ui由LayoutNode来组成

compose ui 和android ui嵌套用法

  1. 在androidview嵌入compose ui
    1. Actvity根布局:在activity调用setContent {}方法
    2. 其他:在其他view嵌入ComposeView,然后调用ComposeView的setContent{}方法
  2. 在compose ui嵌入android view
    1. 在可重组函数内部调用AndroidView可重组函数来嵌入android view

compose ui结构图

  1. android ui和compose ui结构图
image.png
  1. LayoutNode的LayoutNodeWrapper和Modifer链
image.png

绘制流程

compose的ui绘制不依赖android ui控件,是完全实现了一套绘制流程。通过LayoutNode树完成绘制,LayoutNode内部通过LayoutNodeWrapper来层层绘制,每层最终由DrawModifier(Modifier子类)来进行绘制

  1. 绘制方法传递:从android层传递到LayoutNode,LayoutNode内部绘制结束之后继续绘制子LayoutNode
    1. compose从AndroidComposeView的dispatchDraw开始绘制,调用root: LayoutNode的draw方法
    2. LayoutNode的draw方法调用outerLayoutNodeWrapper: LayoutWrapper的draw方法
    3. LayoutNodeWrapper依次向后调用,LayoutNodeWrapper绘制结束后,再依次调用LayoutNode的子节点的draw方法
  2. LayoutNode内部是分层绘制,每层最终由DrawModifier(Modifier子类)来进行绘制
  3. 以上面的Demo为例,给Column底部添加颜色和文字
    1. 添加红色部分代码:


      image.png
    2. 效果如图:


      image.png
    3. 跟踪drawBehind方法


      image.png

      image.png
    4. drawBehind方法最终创建了一个DrawBackgroundModifer(集成自DrawModifer),并把DrawBackgroundModifer通过CombinedModifier连接在modifier链上


      image.png
    5. 之后在给LayoutNode的modifer赋值的时候,判断如果modifier是DrawModifer就用ModifiedDrawNode包装到LayoutNodeWrapper链


      image.png
    6. 调用LayoutNode.draw方法时,从外向内执行LayoutNodeWrapper.performDraw方法,在performDraw方法执行绘制方法块


      image.png
  1. 为什么关闭硬件加速后出现很多LayerView?
    1. 我们看LayerView的实现的OwnedLayer。它定义了缩放、透明度、位移、阴影、旋转等多种ui属性
      image.png
    2. 再看创建layer的代码,如果是硬件加速使用RenderNodeLayer,如果没有硬件加速会使用LayerView
      image.png
    3. 调用Modifier.graphicsLayer { }和Modifier.graphicsLayer()会添加layer

      1. 调用Modifier.graphicsLayer { } 在方法块里边修改ui属性
      2. Modifier.graphicsLayer() 在函数里边传递ui属性

布局流程

compose的布局同样不使用android ui控件布局,是全新的一套布局流程。通过LayoutNode树完成布局,LayoutNode的大小和位置由LayoutNodeWrapper链来确定,最终都是通过Modifier来确定布局的

  1. 从AndroidComposeView触发布局流程,MeasureAndLayoutDelegate来分发布局事件


    image.png

    image.png

    image.png
  2. 测量流程

    1. 测试最终也是从LayoutNodeWrapper层层测量来确定大小的,确定大小之后修改LayoutNodeWrapper相关的Layer大小


      image.png
    2. performMeasure最终调用LayoutModifier.measure来测量

    3. 以Column为例

      1. 通过modifier.requestHeight设置高度180


        image.png
      2. requestHeight实际会创建一个SizeModifier,并且链接到Modifier链


        image.png
      3. SizeModifier继承自LayoutModifer,并且实现了设置测量的功能


        image.png
      4. 在LayoutNode遍历modifer的时候,遇到LayoutModifer后用ModifiedLayoutNode来包装LayoutModifer


        image.png
      5. ModifiedLayoutNode在测量时调用LayoutModifier去测量大小

      6. LayoutModifer之后用自身的Constraints去继续向下层LayoutNodeWrapper测量

      7. 最终测量到最接近LayoutNode的InnerPlaceable(LayoutNodeWrapper子类),在这里会触发测试子LayoutNode


        image.png
      8. 完成测量

    4. 设置位置。

      1. 测试完成之后会调用LayoutNode.place(或者replace)设置位置,最终执行到接口Placealble的placeAt方法
      2. 先调用DelegatingLayoutNodeWrapper的placeAt方法,这里会调用LayoutNodeWrapper的placeAt方法,然后再调用measureResult.placeChildren去设置下层LayoutNodeWrapper的位置


        image.png

        image.png

总结

  1. compose是全新开发的ui框架,不依赖android ui
  2. compose的ui树由LayoutNode构成
  3. LayoutNode其实是简单的数据对象,实际的布局、绘制等都依赖Modifier链
  4. 在缺少硬件加速的情况下,会存在很多ViewLayer的android view对象,他们的父类容器是ViewLayerContainer,ViewLayer都是兄弟节点
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容