UGUI源码分析|05 LayoutRebuilder
主要负责刷新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);
}