UGUI内核大探究(十)Layout与Fitter

本文探究Unity3D的UGUI系统中Layout的重建过程,重点讨论HorizontalLayoutGroup、VerticalLayoutGroup、GridLayoutGroup的工作原理,以及它们与Graphic组件的关系。通过LayoutRebuilder、ILayoutController和ILayoutGroup接口,分析了ContentSizeFitter和AspectRatioFitter如何影响对象尺寸,并揭示了LayoutGroup子类如何布局子对象。

UGUI内核大探究(七)Graphic中我们讲到Graphic组件SetLayoutDirty里会通知LayoutRebuilder布局需要重建,那么布局是具体是怎样重建的呢?我们知道UGUI有三种布局组,HorizontalLayoutGroup(水平布局组)、VerticalLayoutGroup(垂直布局组)和GridLayoutGroup(格子布局组),为对象添加某一种布局组,就可以让其子对象按照对应布局方法排列。那么这些LayoutGroup与Graphic又有什么关系呢?本文就带着这些问题就探究一下Layout系列组件的原理。

按照惯例,附上UGUI源码下载地址

在研究Layout系列组件之前,我们首先要看一下LayoutRebuilder这个类。Graphic是通过MarkLayoutForRebuild这个静态方法标记自己需要重建的。

        public static void MarkLayoutForRebuild(RectTransform rect)
        {
            if (rect == null)
                return;

            RectTransform layoutRoot = rect;
            while (true)
            {
                var parent = layoutRoot.parent as RectTransform;
                if (!ValidLayoutGroup(parent))
                    break;
                layoutRoot = parent;
            }

            // We know the layout root is valid if it's not the same as the rect,
            // since we checked that above. But if they're the same we still need to check.
            if (layoutRoot == rect && !ValidController(layoutRoot))
                return;

            MarkLayoutRootForRebuild(layoutRoot);
        }

这个方法里,会找到父对象中最上层的 ILayoutGroup类型的组件(ValidLayoutGrouplayoutRoot,如果没有找到,便判断一下自己是否是ILayoutController类型(ValidController),然后为符合条件的layoutRoot创建Rebuilder。

        private static void MarkLayoutRootForRebuild(RectTransform controller)
        {
            if (controller == null)
                return;

            var rebuilder = s_Rebuilders.Get();
            rebuilder.Initialize(controller);
            if (!CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild(rebuilder))
                s_Rebuilders.Release(rebuilder);
        }

s_Rebuilders是一个LayoutRebuilder类型的ObjectPool,通过Get方法创建(或从Pool里取出)一个实例rebuilder(再通过Release方法回收这一段内存)。我们看到这段代码调用了TryRegisterCanvasElementForLayoutRebuild,将rebuilder注册到CanvasUpdateRegistry。

LayoutRebuilder继承了ICanvasElement接口。UGUI内核大探究(六)CanvasUpdateRegistry中介绍过当Canvas绘制前会调用CanvasUpdateRegistry,而后者会遍历所有注册到它的ICanvasElement类型的组件,调用他们的Rebuild方法。

        public void Rebuild(CanvasUpdate executing)
        {
            switch (executing)
            {
                case CanvasUpdate.Layout:
                    // It's unfortunate that we'll perform the same GetComponents querys for the tree 2 times,
                    // but each tree have to be fully iterated before going to the next action,
                    // so reusing the results would entail storing results in a Dictionary or similar,
                    // which is probably a bigger overhead than performing GetComponents multiple times.
                    PerformLayoutCalculation(m_ToRebuild, e => (e as ILayoutElement).CalculateLayoutInputHorizontal());
                    PerformLayoutControl(m_ToRebuild, e => (e as ILayoutController).SetLayoutHorizontal());
                    PerformLayoutCalculation(m_ToRebuild, e => (e as ILayoutElement).CalculateLayoutInputVertical());
                    PerformLayoutControl(m_ToRebuild, e => (e as ILayoutController).SetLayoutVertical());
                    break;
            }
        }

PerformLayoutCalculation从m_ToRebuild(也就是保存在rebuilder里layoutRo
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值