13 CustomMultiChildLayout-2
childSize 相信大家都能故名思义,那 childId 是什么呢?
这就要从 MultiChildLayoutDelegate 的实现说起,在 MultiChildLayoutDelegate 内部会有一个 Map _idToChild; 对象,这个 Map 对象保存着 Object id 和 RenderBox 的映射关系,而在 MultiChildLayoutDelegate 中获取 RenderBox 都需要通过 id 获取。
_idToChild 这个 Map 是在 RenderBox performLayout 时,在 delegate._callPerformLayout 方法内创建的,创建后所用的 id 为 MultiChildLayoutParentData 中的 id, 而 MultiChildLayoutParentData 的 id ,可以通过 LayoutId 嵌套时自定义指定赋值。
而完成上述布局,我们需要知道每个 child 的 index ,所以我们可以把 index 作为 id 设置给每个 child 的 LayoutId 。
所以我们可以通过 LayoutId 指定 id 为数字 index , 同时告知 delegate ,这样我们就知道 child 顺序和位置啦。
这个 id 是
Object类型 ,所以你懂得,你可以赋予很多属性进去。
如下代码所示,这样在自定义的 CircleLayoutDelegate 中,就知道每个控件的 index 位置,也就是知道了,圆形布局中每个 item 需要的位置。
我们只需要通过 index ,计算出 child 所在的角度,然后利用 layoutChild 和 positionChild 对每个item进行布局即可,完整代码:GSYFlutterDemo
///自定义实现圆形布局
class CircleLayoutDelegate extends MultiChildLayoutDelegate {
final List<String> customLayoutId;
final Offset center;
Size childSize;
CircleLayoutDelegate(this.customLayoutId,
{this.center = Offset.zero, this.childSize});
@override
void performLayout(Size size) {
for (var item in customLayoutId) {
if (hasChild(item)) {
double r = 100;
int index = int.parse(item);
double step = 360 / customLayoutId.length;
double hd = (2 * math.pi / 360) * step * index;
var x = center.dx + math.sin(hd) * r;
var y = center.dy - math.cos(hd) * r;
childSize ??= Size(size.width / customLayoutId.length,
size.height / customLayoutId.length);
///设置 child 大小
layoutChild(item, BoxConstraints.loose(childSize));
final double centerX = childSize.width / 2.0;
final double centerY = childSize.height / 2.0;
var result = new Offset(x - centerX, y - centerY);
///设置 child 位置
positionChild(item, result);
}
}
}
@override
bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false;
}总的来说,第二种实现方式相对简单,但是也丧失了一定的灵活性,可自定义控制程度更低,但是也更加规范与间接,同时我们自己实现 RenderBox 时,也可以用类似的 delegate 的方式做二次封装,这样的自定义布局会更行规范可控。
学员评价