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 - 可组合项的生命周期,我这边直接说结论,就是状态如果改变了内容则组合会发生改变,影响可组合项的绘制,而内容如果没发生改变,则会跳过重组。

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

kotlin 复制代码
    var state by remember {
        mutableStateOf(0)
    }
    Column() {
        Text(text = "重组$state")
        Text(text = "不重组")
    }

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

测量

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

kotlin 复制代码
    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)
        }
    )

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

绘制

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

为什么≈

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

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

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

Compose的智能取决于写法

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

相关推荐
666xiaoniuzi5 小时前
深入理解 C 语言中的内存操作函数:memcpy、memmove、memset 和 memcmp
android·c语言·数据库
沐言人生10 小时前
Android10 Framework—Init进程-8.服务端属性文件创建和mmap映射
android
沐言人生10 小时前
Android10 Framework—Init进程-9.服务端属性值初始化
android·android studio·android jetpack
沐言人生10 小时前
Android10 Framework—Init进程-7.服务端属性安全上下文序列化
android·android studio·android jetpack
追光天使10 小时前
【Mac】和【安卓手机】 通过有线方式实现投屏
android·macos·智能手机·投屏·有线
小雨cc5566ru10 小时前
uniapp+Android智慧居家养老服务平台 0fjae微信小程序
android·微信小程序·uni-app
一切皆是定数11 小时前
Android车载——VehicleHal初始化(Android 11)
android·gitee
一切皆是定数11 小时前
Android车载——VehicleHal运行流程(Android 11)
android
problc11 小时前
Android 组件化利器:WMRouter 与 DRouter 的选择与实践
android·java