主要负责刷新UI的位置和大小

核心方法就是MarkLayoutForRebuild

MarkLayoutForRebuild

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

    // 这一部分是找到最上层带有ILayoutGroup的父节点
    var comps = ListPool<Component>.Get();
    bool validLayoutGroup = true;
    RectTransform layoutRoot = rect;
    var parent = layoutRoot.parent as RectTransform;
    while (validLayoutGroup && !(parent == null || parent.gameObject == null))
    {
        validLayoutGroup = false;
        parent.GetComponents(typeof(ILayoutGroup), comps);

        for (int i = 0; i < comps.Count; ++i)
        {
            var cur = comps[i];
            if (cur != null && cur is Behaviour && ((Behaviour)cur).isActiveAndEnabled)
            {
                validLayoutGroup = true;
                layoutRoot = parent;
                break;
            }
        }
        parent = parent.parent as RectTransform;
    }

    // 这说明没招到layout或者不合法,直接返回了
    if (layoutRoot == rect && !ValidController(layoutRoot, comps))
    {
        ListPool<Component>.Release(comps);
        return;
    }

    MarkLayoutRootForRebuild(layoutRoot);
    ListPool<Component>.Release(comps);
}

MarkLayoutRootForRebuild

将目标节点注册到CanvasUpdateRegister中

private static void MarkLayoutRootForRebuild(RectTransform controller)
{
    if (controller == null)
        return;
	// 从对象池里拿一个LayoutRebuild
    var rebuilder = s_Rebuilders.Get();
    rebuilder.Initialize(controller);
    if (!CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild(rebuilder))
        s_Rebuilders.Release(rebuilder);
}

Rebuild

实现自接口ICanvasElement

public void Rebuild(CanvasUpdate executing)
{
    switch (executing)
    {
        // 仅在Layout阶段执行
        case CanvasUpdate.Layout:
            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

递归遍历rect,它以及它的子节点上,所有实现了ILayoutElement的节点都需要执行action操作,这个接口交给上层实现

private void PerformLayoutCalculation(RectTransform rect, UnityAction<Component> action)
{
    if (rect == null)
        return;

    var components = ListPool<Component>.Get();
    rect.GetComponents(typeof(ILayoutElement), components);
    StripDisabledBehavioursFromList(components);


    if (components.Count > 0  || rect.GetComponent(typeof(ILayoutGroup)))
    {
        for (int i = 0; i < rect.childCount; i++)
            PerformLayoutCalculation(rect.GetChild(i) as RectTransform, action);

        for (int i = 0; i < components.Count; i++)
            action(components[i]);
    }

    ListPool<Component>.Release(components);
}

PerformLayoutControl

与上面类似,不过这里是先处理父节点中实现了ILayoutSelfController的UI,再处理其他的,最后再递归子节点

private void PerformLayoutControl(RectTransform rect, UnityAction<Component> action)
{
    if (rect == null)
        return;

    var components = ListPool<Component>.Get();
    rect.GetComponents(typeof(ILayoutController), components);
    StripDisabledBehavioursFromList(components);

    if (components.Count > 0)
    {
        for (int i = 0; i < components.Count; i++)
            if (components[i] is ILayoutSelfController)
                action(components[i]);

        for (int i = 0; i < components.Count; i++)
            if (!(components[i] is ILayoutSelfController))
            {
                var scrollRect = components[i];

                if (scrollRect && scrollRect is UnityEngine.UI.ScrollRect)
                {
                    if (((UnityEngine.UI.ScrollRect)scrollRect).content != rect)
                        action(components[i]);
                }
                else
                {
                    action(components[i]);
                }
            }

        for (int i = 0; i < rect.childCount; i++)
            PerformLayoutControl(rect.GetChild(i) as RectTransform, action);
    }

    ListPool<Component>.Release(components);
}