• Compose的一些小Tips - 可组合项的绘制


    系列文章

    Compose的一些小Tips - 可组合项的生命周期
    Compose的一些小Tips - 可组合项的绘制(本文)
    Compose的一些小Tips - 列表的优化

    前言

    本系列介绍Compose的一些常识,了解这些tips并不会让人摇身一变成为大佬,但可以帮助到一些学习Compose的安卓开发者避免一些误区,也是对Compose入门详解中遗漏的一个补充。本文介绍可组合项的绘制

    View的绘制 ≈ 可组合项的绘制

    在说可组合项之前,我们先说说原生view的绘制过程,面试时也经常问到,并且可组合项的展现和view的绘制过程非常的相似。
    在这里插入图片描述

    原生view的绘制

    我们简单复习下原生view的三个主要的阶段,值得注意的是,每一个view的绘制,都会经历这必不可少的三个阶段

    • onMeasure( int , int ) 测量所有子view的大小
    • onLayout( boolean , int , int , int , int ) 再次测量所有子view的大小并分配所有的子元素的位置
    • onDraw(Canvas) 渲染内容

    ps:可以看到onMeasure会进行一次测量,onLayout也会进行一次测量,这是二次测量,有兴趣的可以自行了解,因为和本文内容不相关

    在这里插入图片描述

    可组合项的绘制

    可组合项的展现到屏幕前的三个阶段分别为

    • Composition (组合) ,这是Compose的一大特性,与view的绘制不同,Compose 会运行可组合函数并创建界面说明。
    • Layout(布局) , 测量所有子可组合项并分配所有子可组合项的位置,与view的绘制不同,这里的测量会包含view中onMeasure的测量
    • Drawing(绘制),渲染内容

    在这里插入图片描述

    Compose会根据跟踪可组合项中的状态智能的跳过重组时不必要(没发生过变化)的阶段,这也是Compose对性能的优化。

    可组合项的绘制不同阶段对应的具体方法

    对于view来说onMeasure这些方法都是view的实现方法,而可组合项中好像也没有叫Composition 的方法暴露出来,这是因为Compose都是基于可组合项和它的状态来绘制的,因此三个阶段的具体方法也是二者的结合。并且因为单向数据流的关系,可能会因为对组合的状态的监听而影响到测量和绘制阶段(也可能不影响,下面会举例),所以下面的例子都是讲的直接影响这个阶段的作用域,并不是只影响这个阶段的作用域,希望读者不要误解。

    组合

    @Composable 函数或 lambda 代码块中的状态改变时,Compose会监听到状态的改变并开始重组(重新绘制),这也涉及到可组合项的生命周期相关知识,如果不了解的可以去看下Compose的一些小Tips - 可组合项的生命周期,我这边直接说结论,就是状态如果改变了内容则组合会发生改变,影响可组合项的绘制,而内容如果没发生改变,则会跳过重组。

    我们可以来看一个简单的例子

        var state by remember {
            mutableStateOf(0)
        }
        Column() {
            Text(text = "重组$state")
            Text(text = "不重组")
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在上诉例子中第一个Text会因为state 的改变而进行重新进行组合这个阶段,因此重新进行绘制,如果state 从 0 变为1 ,则在理想情况下第一个Text会经历组合和绘制阶段,但因为Text的大小和位置都没有发生变化,所以会跳过测量阶段,而第二个Text因为不涉及状态的变化,会跳过所有的阶段。

    测量

    我们可以再来看一个简单的例子理解影响到测量阶段的值

        var width by remember {
            mutableStateOf(10.dp)
        }
        var offsetX by remember { mutableStateOf(8.dp) }
        Text(
            text = "位置和大小发生变化",
            modifier = Modifier
                .width(width)
                .offset {
                IntOffset(offsetX.roundToPx(), 0)
            }
        )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在上诉的简单代码示例中,影响到可组合项大小的width 状态和影响到可组合项位置的offsetX 状态会使Compose进行可组合项的测量阶段,然后再进行绘制阶段。

    绘制

    而绘制阶段的最明显的作用范围用最简单的话来说就是每一次从屏幕上肉眼可见的变化,都经历了绘制阶段,包括上面两段示例代码,都经历了绘制阶段。

    为什么≈

    为什么说View的绘制是约等于可组合项的绘制而不是等于呢,上面其实已经吧答案说过了

    • view的绘制阶段是每个阶段都要进行的,哪怕什么都没有变化,只要执行了重绘,所有的阶段都会重新进行一遍。
    • Compose的可组合项会根据状态智能的跳过不需要的阶段,从而进行性能的优化

    这是原生xml和Compose除了写法以外,原理的不同。

    Compose的智能取决于写法

    上面已经说过,Compose的可组合项会根据状态智能的跳过不需要的阶段,以优化性能,而这个智能就很有嚼头,如果写法对了,就会智能的跳过,如果写的不对,就会造成额外的开销,甚至因为Compose的代码还在发展中,还不够成熟,会比原生xml更消耗性能,这也是一些不愿意学习的读者所说的,“我跑起来性能就是比原生差,跨平台还不如选flutter,哪儿哪儿都不行,再看两年吧”。在写法错误情况下,Compose的性能确实不如原生xml的。

  • 相关阅读:
    基于SpringBoot的校园志愿者管理系统
    sqoop学习
    “大厂面试指南”强势上线,隔离30天的我重新开启面试之路!!
    Wnt (wingless) / β-catenin通路中的小分子抑制剂 | MedChemExpress
    springcloud整合seata我踩过的坑
    Roson的Qt之旅 #113 QML布局之StackLayout(堆栈布局)
    Pytest之用例执行--并发执行、重复执行、输出报告
    npm 打包报错JavaScript heap out of memory(亲测可行)
    哪种护眼灯孩子用着最好?盘点五款用眼舒适度高的护眼灯
    成长一夏 挑战赛来袭 专属社区福利来袭~免费获得CSDN定制T恤衫
  • 原文地址:https://blog.csdn.net/shop_and_sleep/article/details/133079527