我们已经知道了各类控件的作用及其使用方法,这些 Widget 被我们开发人员配置了多个属性来定义它的展现形式,例如配置 Text 组件需要显示的字符串,配置输入框组件需要显示的内容。我们 Element 树会记录这些配置信息。熟悉 React 的读者可能了解过其中的 “虚拟 DOM” 这个概念,上述 Flutter 这种操作也正体现了这一概念。Widget 是不可变,它的改变就意味着要重建,而其重建也非常频繁,如果我们将更多的任务都交给它将会对性能造成很大的损伤,因此我们把 Widget 组件当作一个虚拟的组件树,而真正被渲染在屏幕上的其实是 Elememt 这棵树,它持有其对应 Widget 的引用,如果他对应的 Widget 发生改变,它就会被标记为 dirty Element,于是下一次更新视图时根据这个状态只更新被修改的内容,从而达到提升性能的效果。

每次,当控件挂载到控件树上时,Flutter 调用其 createElement() 方法,创建其对应的 Element。Flutter 再将这个 Element 放到元素树上,并持有创建它控件的引用,如下图:

控件会有它的子树:

子控件也会创建相应 Element 被放在元素树上:

4Element 中的状态

我们上文提到了 Widget 的不可变性,相应的 Element 就有其可变性,正如我们前文所说的它被标记为 dirty Element 便是作为需要更新的状态,另外一个我们需要格外注意的是,有状态组件(StatefulWidget)对应的 State 对象其实也被 Element 所管理,如下图所示。

Flutter 中的 Widget 一直在重建,每次重建之后,Element 都会采用相应的措施来确定是否我对应的新控件跟之前引用旧控件是否有所改变,如果没改变则只需要做更新操作,如果前后不同则会重创建。那么,Element 根据什么来确定控件是否改变呢?它会比较 Widget 以下两个属性:

  • 组件类型

  • Widget 的 Key (如果有)

组件类型即前后控件的是否是同一个类所创建的,Key 即为每个控件的唯一标识。

5 渲染树详解

我们已经大致知道 Flutter 中的三棵重要的树及 Element 树的工作原理,其中第三棵渲染树的任务就是做组件的具体的布局渲染工作。

渲染树上每个节点都是一个继承自 RenderObject 类的对象,其由 Element 中的 renderObject 或  RenderObjectWidget 中的 createRenderObject 方法生成,该对象内部提供多个属性及方法来帮助框架层中的组件如何布局渲染。

"

我们知道 StatelessWidget 和 StatefulWidget 两种直接继承自 Widget 的类,在 Flutter 中,还有另一个类 RenderObjectWidget 也同样直接继承自 Widget,它没有 build 方法,可通过 createRenderObject 直接创建 RenderObject 对象放入渲染树中。Column 和 Row 等控件都间接继承自 RenderObjectWidget

"

主要属性和方法如下:

  • constraints 对象,从其父级传递给它的约束

  • parentData 对象,其父对象附加有用的信息。

  • performLayout 方法,计算此渲染对象的布局。

  • paint 方法,绘制该组件及其子组件。

RenderObject 作为一个抽象类。每个节点需要实现它才能进行实际渲染。扩展 RenderOject 的两个最重要的类是RenderBox 和 RenderSliver。这两个类分别是应用了 Box 协议和 Sliver 协议这两种布局协议的所有渲染对象的父类,其还扩展了数十个和其他几个处理特定场景的类,并实现了渲染过程的细节,如 RenderShiftedBox 和 RenderStack 等等。

布局约束

在上面,我们介绍组件渲染流程时,我们了解到了 Flutter 中的控件在屏幕上绘制渲染之前需要先进行布局(Layout)操作。其具体可分为两个线性过程:从顶部向下传递约束,从底部向上传递布局信息,其过程可用下图表示。

第一个线性过程用于传递布局约束。父节点给每个子节点传递约束,这些约束是每个子节点在布局阶段必须要遵守的规则。就好像父母告诉自己的孩子 :“你必须遵守学校的规定,才可以做其他的事”。常见的约束包括规定子节点最大最小宽度或者子节点最大最小的高度。这种约束会向下延伸,子组件也会产生约束传递给自己的孩子,一直到叶子结点。

第二的线性过程用来传递具体的布局信息。子节点接受到来自父节点的约束后,会依据它产生自己具体的布局信息,如父节点规定我的最小宽度是 500 的单位像素,子节点按照这个规则可能定义自己的宽度为 500 个像素,或者大于 500 像素的任何一个值。这样,确定好自己的布局信息之后,将这些信息告诉父节点。父节点也会继续此操作向上传递一直到最顶部。

下面我们具体介绍有哪些具体的布局约束可在树中传递。Flutter 中有两种主要的布局协议:Box 盒子协议和 Sliver 滑动协议。这里我们以盒子协议为例展开具体的介绍。

在盒子协议中,父节点传递给其子节点的约束为 BoxConstraints。该约束规定了允许每个子节点的最大和最小宽度和高度。如下图,父节点传入 Min Width 为 150,Max Width 为 300 的 BoxConstraints:

当子节点接受到该约束,便可以取得上图中绿色范围内的值,即宽度在 150 到 300 之间,高度大于 100,当取得具体的值之后再将取得具体的大小的值上传给父节点,从而达到父子的布局通信。

6 自定义一个 Center 控件

现在,我们可以应用前文中提到的布局约束与渲染树相关的概念自己定义一个类似居中布局的组件 RenderObject 对象渲染在屏幕上。

所以我们称自己自定义组件为 CustomCenter

void main() {

现在我们来实现我们的 CustomCenter:

class CustomCenter extends SingleChildRenderObjectWidget {

CustomCenter 继承了

SingleChildRenderObjectWidget,表明这个 Widget 只能有一个子控件,其中,createRenderObject(…) 方法用于真正创建并返回我们的 RenderObject 对象实例, 我们的 RenderObject 为 RenderCustomCenter,代码如下:

class RenderCustomCenter extends RenderShiftedBox {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

同时我经过多年的收藏目前也算收集到了一套完整的学习资料以及高清详细的Android架构进阶学习导图及笔记免费分享给大家,希望对想成为架构师的朋友有一定的参考和帮助。

下面是部分资料截图,诚意满满:特别适合有开发经验的Android程序员们学习。

资料免费领取方式:点击我的GitHub~

不论遇到什么困难,都不应该成为我们放弃的理由!

-3hBVNDfE-1711182869038)]

资料免费领取方式:点击我的GitHub~

不论遇到什么困难,都不应该成为我们放弃的理由!

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐