系列
UGUI源码分析系列总览
相关前置:
UGUI CanvasUpdateSystem源码分析
文章目录
- 系列
- UML图一览
- LayoutSystem
- LayoutRebuilder
- 标记
- 重建
- 布局组件
UML图一览
LayoutSystem
Related Class: LayoutRebuilder、LayoutGroup、Canvas、CanvasUpdateRegistry、
Related Interface: ILayoutElement、ILayoutController、ILayoutIgnorer、
Intro: CanvasUpdateSystem中更新布局的具体实现系统。
- ILayoutElement: 布局元素,布局的接收方,存储有关布局的信息
- ILayoutController:布局控制接口,布局的实施方,制定布局规则
- ILayoutIgnorer:忽略布局接口,忽略开关开启状态将忽略该物体的布局
LayoutSystem,是UGUI中由CanvasUpdateSystem发起(m_LayoutRebuildQueue中大部分都是LayoutRebuilder)的关于布局排列的处理系统。
本文将从LayoutSystem的响应逻辑切入,并在之后的文章中结合具体的组件来分析整个LayoutSystem。
LayoutRebuilder
标记
UGUI组件(如Graphic、ScrollRect…)在需要布局处理时会通过标记的方式将自身RectTransform封装成一个LayoutRebuilder对象添加进CanvasUpdateSystem中的布局队列(LayoutRebuildQueue)中等待被重建。
public static void MarkLayoutForRebuild(RectTransform rect)
{
if (rect == null || rect.gameObject == null)
return;
var comps = ListPool<Component>.Get();
bool validLayoutGroup = true;
RectTransform layoutRoot = rect;
var parent = layoutRoot.parent as RectTransform;
//从物体父级路径寻中寻找是否存在布局组件(ILayoutGroup)
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;
}
// 检查自身是否满足布局要求
if (layoutRoot == rect && !ValidController(layoutRoot, comps))
{
ListPool<Component>.Release(comps);
return;
}
//添加进CanvasUpdateSystem中
MarkLayoutRootForRebuild(layoutRoot);
ListPool<Component>.Release(comps);
}
private static void MarkLayoutRootForRebuild(RectTransform controller)
{
if (controller == null)
return;
//生成一个rebuilder对象
var rebuilder = s_Rebuilders.Get();
//初始化数据
rebuilder.Initialize(controller);
//将rebuilder对象注册进CanvasUpdate中,等待Canvas的重建命令
if (!CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild(rebuilder))
s_Rebuilders.Release(rebuilder);
}
重建
当重建指令触发时(具体详情),LayoutRebuilder将对自身即其子级路径中的所有ILayoutElement与ILayoutController执行相应的接口。
//CanvasUpdateSystem触发重建
public void Rebuild(CanvasUpdate executing)
{
switch (executing)
{
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;
}
}
布局组件
经过对LayoutRebuilder的分析,布局接口的触发规则已经被摸清楚了。虽然UGUI组件中有一些组件都继承了ILayoutElement接口(例如:Image,Text,ScrollRect,InputField),但它们并不会涉及对接口方法的实现。这是因为这些组件主要是布局操作的接收方,只需要通过该接口被布局实施方所发现即可。而UGUI中负责这些接收物体的布局设置功能主要是由LayoutGroup衍生的子类组件来完成。
简单UML图:
在下一篇文章中,作者会从LayoutGroup切入,分析布局UI的具体逻辑。
.
.
.
.
.
嗨,我是作者Vin129,逐儿时之梦正在游戏制作的技术海洋中漂泊。知道的越多,不知道的也越多。希望我的文章对你有所帮助:)