注意:为了让分析更加简单,和逻辑清晰,咱们去掉了部分源码和注释,只留下了主要的代码和逻辑。若是没有看过上一篇文章,请点击下面的连接。 Flutter必须理解Widget、Element、RenderObject的关系(一)git
接着上篇接着来,上面提到过这个这个方法比较重要,咱们将单独拿一个章节讲解,下面是updateChild()源码。github
abstract class Element extends DiagnosticableTree implements BuildContext { Element updateChild(Element child, Widget newWidget, dynamic newSlot) { if (newWidget == null) { if (child != null) deactivateChild(child);//注释1 return null; } if (child != null) { if (child.widget == newWidget) { if (child.slot != newSlot) updateSlotForChild(child, newSlot);//注释2 return child; } if (Widget.canUpdate(child.widget, newWidget)) { if (child.slot != newSlot) updateSlotForChild(child, newSlot); child.update(newWidget);//注释3 return child; } deactivateChild(child); } return inflateWidget(newWidget, newSlot);//注释4 } } 复制代码
这个方法的意思就更新Element配置的一个函数,具体是怎么更新的呢,其实仍是比较简单的,在前面的的文章中咱们说过关于Element树的概念,这个函数就是从树中移除相关的数据。markdown
注释1ide
deactivateChild()是把Element从Element树上删除,函数
分析到这里咱们先告一个小段落,如今终结一下Element。oop
仍是先从概念上入手,下面是ReaderObject的概念。布局
An object in the render tree.post
这个概念很简单,大概的意思是渲染树上的一个对象。从概念上得出每一个RenderObject都挂在一个渲染树上,咱们看一下RenderObject的源码,下面是RenderObject的源码。ui
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { ParentData parentData; Constraints _constraints; void layout(Constraints constraints, { bool parentUsesSize = false }) { } void paint(PaintingContext context, Offset offset) { } void performLayout(); void markNeedsPaint() { } } 复制代码
咱们共提供这些方法可用看出来,RenderObject的主要职责是绘制和布局,也就是咱们说的,这是一个真正的渲染对象,咱们下面分析一下这个对象是怎么布局和渲染的。this
若是咱们想在屏幕上想画一个红色的正方形,两个重要的问题须要解决,第一是,画在哪里?第二是,是怎么画?画在哪里就是布局,怎么画就是渲染,下面咱们对着两个问题进行分析,先从布局开始。
咱们先从layout入手,这个方法是计算渲染对象的大小和布局,这方法一般不要被在类覆盖,若是你想重写布局,要重写performLayout()。
abstract class RenderObject { void layout(Constraints constraints, { bool parentUsesSize = false }) { RenderObject relayoutBoundary;//注释1 if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) {//注释2 relayoutBoundary = this;//注释3 } else { final RenderObject parent = this.parent; relayoutBoundary = parent._relayoutBoundary;//注释4 } _relayoutBoundary = relayoutBoundary; if (sizedByParent) { performResize();//注释5 } performLayout();//注释6 markNeedsPaint(); } } 复制代码
上面就是layout方法,这方法有两个参数,第一个参数constraints,是经过父类传入的,也就是你们常说的约束从上到下。
注释1
relayoutBoundary这变量比较重要,咱们打算用用个小结讲解。
在注释1处声明了一个属性relayoutBoundary,属性叫作布局边界,这个这属性是提升渲染效率的(由于在布局和渲染的都会用到这属性),咱们知道RenderObject在渲染树中,如今若是一个叶子RenderObject对象发生布局变化,那么必定会致使这个叶子节点的父布局重新布局,这一定致使低效,那么Flutter就用这个属性防止父节点重新布局,可是这个须要知足几个条。
parentUsesSize
layout的第二个参数,父控件的布局是依赖子控件的布局,默认值是false,也就是默认父控件不依赖子控件布局。
sizedByParent
子控件的大小彻底在父控件的约束条件下,也就是子控件在父控件的min和max之间。
constraints.isTight
就是min等于max,这比较容易理解。
parent is! RenderObject
这个条件很容易理解了,就是parent不是RenderObject。
上面的4个条件,若是一个成立就执行注释3的代码,注释3的意思布局边界是本身,由于这个节点的布局变化不会引发父节点的重新布局。不然执行注释4,把父节点的布局边界赋值给本身。
performResize()方法子类实现的方法,这个方法主要的功能是更新渲染对象的大小,固然这个这个方法被调用的条件是sizedByParent是true,也就是说子控件的大小彻底在父控件的约束条件下,执行这个方法。
performLayout()方法一样的是须要子类去实现的,计算RenderObject的布局。
下面咱们分析一下RenderConstrainedBox的performLayout方法,仍是先看一下RenderConstrainedBox的继承关系。
class RenderConstrainedBox extends RenderProxyBox { } class RenderProxyBox extends RenderBox { } abstract class RenderBox extends RenderObject { } 复制代码
从上面的代码中很明显能看出来RenderConstrainedBox最终继承RenderObject,咱们下面就看一下performLayout()方法的具体实现。
class RenderConstrainedBox extends RenderProxyBox { @override void performLayout() { if (child != null) { child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true) size = child.size; } else { size = _additionalConstraints.enforce(constraints).constrain(Size.zero); } } } 复制代码
这个方法能看出来,当调用RenderObject的layout()方法的时候,会调用RenderConstrainedBox的performLayout(),这个方法又会调用child.layout()方法,直到布局完成。约束是从父类传到子类的,可是size是从子类给到父类的,用一张图表示就清楚多了,这个就是你们常说的,constraints从上到下,size从下到上。
在RenderObject中主要渲染的的函数是paint(),在RenderObject中没有具体的实现,须要子类本身实现,下面是paint方法。
void pait(PaintingContext context, Offset offset) { } 复制代码
上面的paint有两个参数,先解释两个参数的意思,让后天然就能明白这个函数的意思。
context
Context是PaintingContext,这类继承自ClipContext,在ClipContext中有一个Canvas,这样好像就理解了,原来context是用来画东西的画布。
offset
offset是Offset,就是屏幕坐标上的一个位置。
这样咱们应该能猜到这个paint()就是一个,将一个context,画到一个屏幕位置上,这里你们可能有一个疑问,好比咱们想画一个红色的正方形,如今只有画布和画到那个位置,尚未告诉我红色方方块的大小呢,怎么画?在上一小节中(RenderObject布局),咱们讲了performResize()这个函数,这个函数已经计算出红色方块的大小了。