从UI系统的设计角度认识flutter三棵树

前言

刚接触flutter开发的同学肯定对一个概念不陌生:三棵树,分别是widget树,element树和renderobject树。然后大家就开始搜三棵树的各种原理,创建流程了。但是,今天我想从一个不同的角度来谈谈这三棵树的认知,那就是设计思想角度。为什么是三颗树呢?一棵树不行吗,四颗不行吗?

常见UI设计思路

在现在的可视化操作系统中,基本上UI系统的设计思路是大同小异的,所谓的UI系统要解决的问题就是三个(这里我们将一块要显示的区域称为view):

  • view有多大?
  • view放在哪里?
  • view长什么样?

继续抽象下,就对应三个概念:测量,布局,绘制。其中测量比较特殊,我们想下,一个要展示的区域大小由什么决定?其实有两点决定:上一级view的约束以及下一级view的大小。首先是上一级view的约束,比如我不希望我的子view大小超过我的大小,或者我希望子view只占据我大小的二分之一等等,之所以还收到子view大小的影响,我们考虑这样一个场景:一个view的大小随它的子view大小改变而改变,最常见的例子就是文本展示view,当文本太长换行时,我们自然希望文本view高度自动变高,从而能完美展示所有文本。基于这些考虑,我们发现view可以很容易的组织成下面的结构:


p1.png

可以看到,在这样的UI设计体系中,View2的是受到上一级view和下一级view共同影响的,这其实是实际需求决定的。

树状UI体系的缺点

上面我们从实际需求出发,探讨了现代UI设计为什么是这种树形结构,但事实上这种结构有它的弊端,那就是更新效率不高!我们设想下,如果上图中的view2需要更新,该如何做?那就是必须遍历整颗view树,将所有的测量,布局,绘制流程重新走一遍。因为view2的大小改变了,它会同时影响上级view和下级所有的view。

这个时候,我们再设想一个很普通的场景,一次网络请求回来,我们要给界面设置数据:

textView1.setText("text1");
textView2.setText("text2");
textView3.setText("text3");
……

可能大家没有意识到,事实上,每一次setText都会触发整棵view树的遍历过程,主要是为了计算哪些区域需要重新绘制,便于在下一次垂直同步信号来临时重新绘制整棵view树(包含测量,布局,绘制三个过程)。这里三次调用setText,就意味着整棵树被遍历了三遍,现实开发中可能会有更多的setText,那被遍历的次数就更多了,这就是树状UI体系的缺点了。

一棵树到两棵树

仔细分析上面的重复遍历整颗view数的过程,如果要优化该怎么做呢?最简单的方式就是等所有的setText方法结束后,统一来一次遍历更新就好了。要实现这个需求就意味着我们的setText()方法将不再触发ui的刷新逻辑。
其实仔细分析下,TextView中的text只是一个文本属性,这个属性改变的时候确实会有界面的改变,但是在连续多个TextView的text改变时,我们不希望立即刷新界面,只希望最后一次刷新就好了。这样的话,我们完全可以将TextView的职责做个拆分:


p2.png

这样,我们将同时负责text记录和界面刷新的TextView拆分成了两个,Widget负责记录text,RenderObject负责真正的展示UI和刷新。用这种思路,我们大可以尽情随意设置Widget中的text,代价非常低,最后只需要执行一次界面刷新就可以了。

总结下这种思想,就是职责分离,负责界面展示的的就负责界面展示,负责记录控件配置的就记录控件配置,配置的改变并不一定需要改变界面的展示。按照这种思想,我们原有的树状结构一棵view树就演变成了下面这个样子:


p3.png

两棵树到三棵树

有了两棵树的结构,我们确实解决了UI刷新时重复遍历的问题,那这样就够了嘛?我们把目光聚焦在Widget树上,以Widget2为例,它有可能会发生以下变化:

  • Widget2中某个内容变化
    这种情况我们需要直接更新RenderObject2就好(可能需要遍历整颗RenderObject树)
  • Widget2的子Widget3被移除
    这时我们需要对应的移除RenderObject3
  • Widget2的子Widget3被Widget5替换
    这个时候我们不需要移除RenderObject3然后补上一个RenderObject5,我们可以直接复用RenderObject3,这样代价最小。

上面是随意列出的一些情况,我们发现,两棵树的情况下,Widget树的变化如何对应到RenderObject树上的变化是一个比较特殊的过程,它不能直接映射过去,而是可以做各种优化。那么,问题来了,这个特殊的映射过程谁来负责?

我们首先考虑由Wiget来控制自己对应的RenderObject可不可以。我们假设Widget2要被从Widget树中移除,这个时候Widget2应该找到对应的RenderObject2,然后遍历所有RenderObject2的子RenderObject,分别移除它们。怎么样,发现问题没有?我们将一棵树拆成了两棵树,就是为了避免在设置一些控件的配置时引起整颗view树的遍历,但这里又开始进行遍历RenderObject树了,如果使用这种方式的话,拆成两棵树就没有意义了。

为了解决这个尴尬的情况,我们希望能有一个新的角色出现,它负责监控整个Widget树的变化,然后通过一些列的计算,以最小的代价去改动RenderObject树,这样,我们依然可以随意的以极低的成本去修改控件的某些配置了。我们将这个角色称为Element,由于要监控整个Widget树的变化,那么最简单就是每个Widget都有自己的Element。因此,两棵树的结构就演变成了三棵树的结构:


p4.png

至此,我们一步步推导出了flutter三棵树的意义:

  • Widget树 控件的配置信息,不涉及渲染,更新代价极低。
  • RenderObject 树 真正的UI控件树,负责渲染UI,更新代价极大。
  • Element树 Widget树和RenderObject树之间的粘合剂,负责将Widget树的变更以最低的代价映射到RenderObject树上。

结语

事实上,三棵树的存在并不是这里描述的这么简单,这里我只是提供了一种自己理解的思路来诠释三棵树存在的意义,让大家对flutter的UI架构有一个大致的了解。比如我们知道了RenderObject树存在就是为了渲染界面,那么我们是不是可以抛弃Widget树和Element树,自己基于RenderObject树开创一套全新的UI编写方式呢?当然,限于本人水平有限,这里的一些理解可能不一定对,这里欢迎大家指出来,

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容