系列

UGUI源码分析系列总览
相关前置:
UGUI CanvasUpdateSystem源码分析
UGUI源码分析:LayoutSystem布局系统

文章目录

  • 系列
  • UML图一览
  • LayoutGroup
  • 属性介绍
  • 布局过程

UML图一览


LayoutGroup

BaseClass: UIBehaviour

Interface: ILayoutElement, ILayoutGroup

Intro:布局系统的实施组件的基类

虽然UGUI组件中有一些组件都继承了ILayoutElement接口,但并不会涉及对接口方法的实现。这是因为这些组件主要是布局操作的接收方,只需要通过该接口被布局实施方所发现即可

LayoutGroup,是布局组件的基类(GridLayoutGroup、HorizontalOrVerticalLayoutGroup)。本文主要针对纵横布局组件(HorizontalLayoutGroup、VerticalLayoutGroup)进行分析。

属性介绍

  • Padding:内部边距,调整实际用于布局区域的大小
  • Spacing :子物体直接的间隔
  • Child Alignment :子物体对齐方式
  • Child Controls Size :组件控制子物体尺寸开关,开启时组件可以更改物体尺寸。
  • Child Force Expand :组件控制子物体填充区域开关,若可以修改尺寸则会改变子物体尺寸填充区域,若不可以修改尺寸,则根据区域大小均衡分布子物体。

布局过程

接下来,作者以HorizontalLayoutGroup为例,逐步进行布局实现的分析。

延续LayoutRebuilderRebuild方法(详情:UGUI源码分析:LayoutSystem布局系统),首先被执行的是ILayoutElementCalculateLayoutInputHorizontal方法。该方法将收集其子节点下所有没有被标记ignoreLayout的物体(m_RectChildren)。

// HorizontalLayoutGroup
public override void CalculateLayoutInputHorizontal()
{//  基类(LayoutGroup)方法base.CalculateLayoutInputHorizontal();CalcAlongAxis(0, false);
}
//LayoutGroup
public virtual void CalculateLayoutInputHorizontal()
{//清空list,准备收集子节点下没有被ignoreLayout的物体m_RectChildren.Clear();var toIgnoreList = ListPool<Component>.Get();for (int i = 0; i < rectTransform.childCount; i++){var rect = rectTransform.GetChild(i) as RectTransform;if (rect == null || !rect.gameObject.activeInHierarchy)continue;rect.GetComponents(typeof(ILayoutIgnorer), toIgnoreList);if (toIgnoreList.Count == 0){m_RectChildren.Add(rect);continue;}for (int j = 0; j < toIgnoreList.Count; j++){var ignorer = (ILayoutIgnorer)toIgnoreList[j];if (!ignorer.ignoreLayout){m_RectChildren.Add(rect);break;}}}ListPool<Component>.Release(toIgnoreList);m_Tracker.Clear();
}

CalcAlongAxis 主要是做LayoutGroup的一些初始化参数的计算。

//LayoutGroup
protected void CalcAlongAxis(int axis, bool isVertical)
{float combinedPadding = (axis == 0 ? padding.horizontal : padding.vertical);bool controlSize = (axis == 0 ? m_ChildControlWidth : m_ChildControlHeight);bool childForceExpandSize = (axis == 0 ? childForceExpandWidth : childForceExpandHeight);float totalMin = combinedPadding;float totalPreferred = combinedPadding;float totalFlexible = 0;bool alongOtherAxis = (isVertical ^ (axis == 1));for (int i = 0; i < rectChildren.Count; i++){RectTransform child = rectChildren[i];float min, preferred, flexible;GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible);if (alongOtherAxis){//另一条轴的情况简单处理,取其中最大的子物体的值即可totalMin = Mathf.Max(min + combinedPadding, totalMin);totalPreferred = Mathf.Max(preferred + combinedPadding, totalPreferred);totalFlexible = Mathf.Max(flexible, totalFlexible);}else{//目标轴处理,数值为子物体数值的累加totalMin += min + spacing;totalPreferred += preferred + spacing; //包括间隔// Increment flexible size with element's flexible size.totalFlexible += flexible;}//去掉多余的一次间隔if (!alongOtherAxis && rectChildren.Count > 0){totalMin -= spacing;totalPreferred -= spacing;}totalPreferred = Mathf.Max(totalMin, totalPreferred);//根据轴设置 m_TotalXXX值SetLayoutInputForAxis(totalMin, totalPreferred, totalFlexible, axis);
}

STEP2:接着会执行ILayoutControllerSetLayoutHorizontal方法。这在GridLayoutGroupHorizontalLayoutGroupVerticalLayoutGroup中有不同的处理。

//HorizontalLayoutGroup
public override void SetLayoutHorizontal()
{//根据轴设置子物体的布局SetChildrenAlongAxis(0, false);
}

LayoutGroup组件如何调整子物体的位置与大小一句话概括:利用了Unity RectTransform中的一个方法

SetInsetAndSizeFromParentEdge(RectTransform.Edge edge, float inset, float size);

布局物体的方法主要是在 选择出目标边(Edge),计算出距离(inset),计算出子物体的大小(size)

//LayoutGroup
protected void SetChildrenAlongAxis(int axis, bool isVertical)
{//获取跟坐标轴有关的设置float size = rectTransform.rect.size[axis];bool controlSize = (axis == 0 ? m_ChildControlWidth : m_ChildControlHeight);bool childForceExpandSize = (axis == 0 ? childForceExpandWidth : childForceExpandHeight);float alignmentOnAxis = GetAlignmentOnAxis(axis);bool alongOtherAxis = (isVertical ^ (axis == 1)); // 当二者不同时为true  例(水平 y轴,垂直 x轴)if (alongOtherAxis){//在水平或垂直布局中,另外一条轴的布局操作相对简单一些//实际尺寸,根据padding计算float innerSize = size - (axis == 0 ? padding.horizontal : padding.vertical);for (int i = 0; i < rectChildren.Count; i++){RectTransform child = rectChildren[i];float min, preferred, flexible;//获取子物体的尺寸,最小、合适、灵活尺寸GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible);//若强制填充,则会以该部件组件的尺寸来决定,反之则以子物体的最佳尺寸float requiredSpace = Mathf.Clamp(innerSize, min, flexible > 0 ? size : preferred);//计算距离边的距离float startOffset = GetStartOffset(axis, requiredSpace);if (controlSize){// 根据轴选取矩形的边,以及距离、尺寸,设置子物体的位置(API:SetInsetAndSizeFromParentEdge)SetChildAlongAxis(child, axis, startOffset, requiredSpace);}else{float offsetInCell = (requiredSpace - child.sizeDelta[axis]) * alignmentOnAxis;SetChildAlongAxis(child, axis, startOffset + offsetInCell);}}}else{//起始位置:对于边的距离float pos = (axis == 0 ? padding.left : padding.top);if (GetTotalFlexibleSize(axis) == 0 && GetTotalPreferredSize(axis) < size)pos = GetStartOffset(axis, GetTotalPreferredSize(axis) - (axis == 0 ? padding.horizontal : padding.vertical));//差值float minMaxLerp = 0;if (GetTotalMinSize(axis) != GetTotalPreferredSize(axis))minMaxLerp = Mathf.Clamp01((size - GetTotalMinSize(axis)) / (GetTotalPreferredSize(axis) - GetTotalMinSize(axis)));float itemFlexibleMultiplier = 0;if (size > GetTotalPreferredSize(axis)){if (GetTotalFlexibleSize(axis) > 0)itemFlexibleMultiplier = (size - GetTotalPreferredSize(axis)) / GetTotalFlexibleSize(axis);}for (int i = 0; i < rectChildren.Count; i++){RectTransform child = rectChildren[i];float min, preferred, flexible;GetChildSizes(child, axis, controlSize, childForceExpandSize, out min, out preferred, out flexible);float childSize = Mathf.Lerp(min, preferred, minMaxLerp);childSize += flexible * itemFlexibleMultiplier;if (controlSize){// 根据轴选取矩形的边,以及距离、尺寸,设置子物体的位置(API:SetInsetAndSizeFromParentEdge)SetChildAlongAxis(child, axis, pos, childSize);}else{float offsetInCell = (childSize - child.sizeDelta[axis]) * alignmentOnAxis;SetChildAlongAxis(child, axis, pos + offsetInCell);}//更新距离,累计子物体尺寸与间隔pos += childSize + spacing;}}
}

到此,纵横布局组件的布局分析就完毕啦,下篇文章作者会对GridLayoutGroup(网格布局组件)以及ILayoutController另一个衍生ContentSizeFitter(尺寸调节组件)进行详细分析。


.
.
.
.
.


嗨,我是作者Vin129,逐儿时之梦正在游戏制作的技术海洋中漂泊。知道的越多,不知道的也越多。希望我的文章对你有所帮助:)


UGUI源码分析:LayoutGroup中的纵横布局组件(HorizontalOrVerticalLayoutGroup)相关推荐

  1. UGUI源码分析:GridLayoutGroup网格布局组件与ContentSizeFitter尺寸调节组件

    系列 UGUI源码分析系列总览 相关前置: UGUI CanvasUpdateSystem源码分析 UGUI源码分析:LayoutSystem布局系统 UGUI源码分析:LayoutGroup中的纵横 ...

  2. UGUI源码分析:开关组件Toggle与ToggleGroup

    系列 UGUI源码分析系列总览 相关前置: UGUI EventSystem源码分析 UGUI源码分析:Selectable交互组件的基类 文章目录 系列 Toggle Toggle组件属性介绍 初始 ...

  3. 集合的get方法中参数从多少开始_源码分析CopyOnWriteArrayList 中的隐藏知识,你Get了吗?...

    欢迎点击 "未读代码" ,关注公众号,文章每周更新 杭州-阿里园区墙 前言 本觉 CopyOnWriteArrayList 过于简单,寻思看名字就能知道内部的实现逻辑,所以没有写这 ...

  4. c+智能指针源码分析_C ++中的智能指针

    c+智能指针源码分析 In this article, we'll take a look at how we can use Smart Pointers in C++. 在本文中,我们将研究如何在 ...

  5. android 代码 drawable,Android Drawable完全解析(一):Drawable源码分析(中)

    呃...我不是故意要凑篇幅写个什么上下篇,实在是因为Drawable源码有点长,一篇写不下啦O(∩_∩)O~ 鉴于源码一般较长,以后所有源码分析的部分,英文注释非必要情况都不再保留! 2:Drawab ...

  6. Ant Design源码分析(三):Wave组件

    Wave组件效果预览 在上一篇文章Button组件的源码分析中遇到了一个Wave组件, Wave组件在Ant design中提供了通用的表单控件点击效果,在自己阅读源码之前,也并没有过更多留心过在这些 ...

  7. F2FS源码分析-1.4 [F2FS 元数据布局部分] Segment Infomation Table-SIT结构

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node ...

  8. F2FS源码分析-1.6 [F2FS 元数据布局部分] Segment Summary Area-SSA结构

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node ...

  9. F2FS源码分析-1.3 [F2FS 元数据布局部分] Checkpoint结构

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node ...

最新文章

  1. html5引入外联样式的优先级,CSS的4种引入方式及优先级
  2. 不同vlan之间如何ping通_【丰润达.安防百科】如何实现交换机不同VLAN、不同网段之间互访?...
  3. 滑盖、双屏手机降价至冰点,为何仍无人问津?
  4. H2K-一种鲁棒且较佳的花生叶疾病检测和分类方法
  5. [转载] python实现堆排序用类的方法_python实现堆排序的实例讲解
  6. JAVA基础之变量(数据类型及其转换)
  7. Android--数据存储
  8. resetlog oracle,用RMAN的全备恢复resetlog之前的数据
  9. 数据结构视频教程-绝对是史上最全的,共30个!!
  10. 捷速pdf修改器如何在pdf中添加附件
  11. 软件文档的类型有哪些?
  12. 【HNOI2015】落忆枫音
  13. gerrit服务器邮箱设置(三)
  14. WorldFirst万里汇推出港币和离岸人民币账户!
  15. grpc-go源码剖析二十之grpc客户端帧接收器是如何处理不同的帧的?
  16. 分布式tensorflow测试代码
  17. SQL Server计算一年中的第几周
  18. 双通道中频信号数字下变频及相位差估计(FPGA)
  19. **Hadoop Ubuntu系统搭建攻略全详细!!!附带Hadoop搭建成功后测试案例**
  20. Linux之——UltraISO写入引导扇区时弹出“找到多于1个分区”解决方法

热门文章

  1. 协会的会员单位宝付,受邀参加支付清算法务培训班
  2. 华为交换机PC+Phone模式,查找座席电话号码
  3. 最美丽的理论:爱因斯坦引力场方程的推导
  4. netty系列之:netty中的frame解码器
  5. 净利润下滑13%,帅丰电器已掉队?
  6. 从windows换到Linux Mint(二)——Linux干啥都得装软件!windows还是香啊!
  7. 智慧冷库物联网云平台方案
  8. 【整理】【持续完善】物联网星座项目介绍
  9. Android 11 从沙盒拷贝文件到外部共享存储区域
  10. matlab 正版贵吗,有人使用正版MATLAB吗?