Flutter自定义View,MultiChildRenderObejctWidget

前言

上回在Flutter自定义View以及响应式UI框架原理中讲了Flutter的响应式UI框架原理以及如何自定义叶子节点LeafRenderObjectWidget,这次我们再来尝试一下如何自定义一个可以包含多个子控件的父布局。

首先呢,我们先给自己定一个目标:

  1. 所有child按角度平均分布到一个圆环上, 以圆环最高点为起点, 顺时针排列;
  2. 保证所有的child不能重叠;
  3. 以子View中最大的宽高为标准计算ViewGroup宽高, 即较小child的宽高也按照子View中最大的宽高计算;

效果如下:

具体实现

根据我们上一篇文章说明,自定义View需要同时构建Widget、Element、RenderObject三个层次,下面就依次说明下,同时我会与自定义叶子节点的对应步骤做同步比较。

Widget

自定义Widget我们是直接继承了RenderObjectWidget,虽然系统给我们提供了LeafRenderObjectWidget以及MultiChildRenderObjectWidget,不过这两个类很简单,直接继承RenderObjectWidget有利于我们了解其原理。

我们可以在这里看到这两个类的比较:SixStarWidget和RingWidgte。

它们两个的主要区别在于:

  1. LeafRenderObjectWidget需要在updateRenderObject中处理RenderObject的更新,MultiChildRenderObjectWidget则不需要(原因在下面Element中介绍)
  2. MultiChildRenderObjectWidget中除了其他自定义参数,需要包含一个List<Widget> children,此参数用于提供给Element使用。

Element

与Widget类似的,我们也是直接继承了RenderObjectElement,而不是LeafRenderObjectElement和MultiChildRenderObjectElement。

两个类的比较:SixStarElement和RingElement。

它们的区别是:

  1. MultiChildRenderObjectElement需要提供insertChildRenderObjectmoveChildRenderObjectremoveChildRenderObjectvisitChildren等方法;
  2. mount以及update方法中,MultiChildRenderObjectElement需要遍历_children分别执行,而LeafRenderObjectElement只需要处理自身即可。

关于以上方法的具体作用请见RingElement内的注释。

RenderObject

两个类的比较:SixStarRenderObject和RingRenderObject。

在上一篇文章中,我按个人理解把SixStarRenderObject分为了四部分,它们在两种RenderObject内的表现有所不同:

  1. paint以及其他属性参数:类似;
  2. layoutpaint, performLayoutperformResize

    • SixStarRenderObject:paint绘制自身,performLayout中赋值自身size即可;
    • RingRenderObject:paint除了绘制自身之外,还需要调用RenderBoxContainerDefaultsMixin#defaultPaint方法处理children的绘制;performLayout中除了赋值自身size之外,还需要遍历children分别执行layout,然后处理偏移量,把各个child按既定规则放在具体位置;

  3. isRepaintBoundary, sizedByParent:一致,即isRepaintBoundary为true,sizedByParent为false
  4. hitTest, hitTestSelf, hitTestChildren

    • SixStarRenderObject:需要支持点击时仅需要重写hitTestSelf为true即可;
    • RingRenderObject:不仅需要重写hitTestSelf为true,还需要在hitTestChildren中调用RenderBoxContainerDefaultsMixin#defaultHitTestChildren方法处理children的点击。

除此之外,在RingRenderObject中还需要重写额外两组方法:

  1. 计算固有大小相关方法compute(Max/Min)Intrinsic(Height/Width):它们的作用是在layout方法未执行时根据给定宽高返回固定的高宽;

    其实LeafRenderObject也存在这一系列方法,只不过作为叶子节点可以认为未经过layout计算时宽高都为0,但是对于ViewGroup我们需要考虑其child,所以需要重写;

  2. setupParentData(RenderBox child)方法:给child指定ParentData,此参数用于传递父布局所提供的数据,包括偏移量以及下一个节点指针等。

关于以上方法的具体作用请见RingRenderObject。

总结

自定义可以添加多个子View的MultiChildRenderObejctWidget以及叶子节点LeafRenderObejctWidget的流程基本一致,而且比较清晰,而且源码中大多提供了默认实现,如果只为了实现功能的话需要重写的方法会更少,比如Element可以直接使用系统提供类,RenderObejct类也可以省略大部分方法。

附Demo地址:github.com/YouCii/Flut…

以上是 Flutter自定义View,MultiChildRenderObejctWidget 的全部内容, 来源链接: utcz.com/a/19426.html

回到顶部