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都是兄弟节点

你可能感兴趣的:(Compose UI)