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

系列

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

文章目录

  • 系列
  • UML图一览
  • GridLayoutGroup
    • 属性介绍
    • 布局过程
  • ContentSizeFitter
    • 属性介绍
    • 实现过程


UML图一览

UGUI源码分析:GridLayoutGroup网格布局组件与ContentSizeFitter尺寸调节组件_第1张图片

GridLayoutGroup

BaseClass: LayoutGroup

Interface: 间接继承了ILayoutElement, ILayoutGroup

Intro:布局系统中的网格布局组件

GridLayoutGroup,是LayoutGroup衍生的用于网格布局的组件,不同于纵横布局,网格布局中严格要求了对子物体尺寸的设置。

属性介绍

UGUI源码分析:GridLayoutGroup网格布局组件与ContentSizeFitter尺寸调节组件_第2张图片

  • Padding:内部边距,调整实际用于布局区域的大小
  • Cell Size:子物体尺寸,设置被布局物体的尺寸
  • Spacing :子物体直接的间隔
  • Start Corner :起始位置,子物体起始放置的位置(四个角)
  • Start Axis :起始轴,优先按照横向/纵向排布
  • Child Alignment :子物体对齐方式
  • Constraint :约束类型,可以限制行列数

布局过程

STEP1:依然是延续LayoutRebuilderRebuild方法,大致流程如HorizontalOrVerticalLayoutGroup相似(详情),不同点在于纵横组件只针对自身的单一轴进行布局,而网格组件则要涵盖两条轴上的布局逻辑。首先被执行的是ILayoutElementCalculateLayoutInputHorizontal方法。

public override void CalculateLayoutInputHorizontal()
{
    //LayoutGroup 基类方法
    base.CalculateLayoutInputHorizontal();
    //若对排列有约束限制,则初始化设置参数
    //这里是横轴则只获取列数的限制
    //CalculateLayoutInputVertical中则会获取minRows
    int minColumns = 0;
    int preferredColumns = 0;
    if (m_Constraint == Constraint.FixedColumnCount)
    {
        minColumns = preferredColumns = m_ConstraintCount;
    }
    else if (m_Constraint == Constraint.FixedRowCount)
    {
        minColumns = preferredColumns = Mathf.CeilToInt(rectChildren.Count / (float)m_ConstraintCount - 0.001f);
    }
    else
    {
        minColumns = 1;
        preferredColumns = Mathf.CeilToInt(Mathf.Sqrt(rectChildren.Count));
    }
    //同HorizontalOrVerticalLayoutGroup组件,初始化参数
    SetLayoutInputForAxis(
        padding.horizontal + (cellSize.x + spacing.x) * minColumns - spacing.x,
        padding.horizontal + (cellSize.x + spacing.x) * preferredColumns - spacing.x,
        -1, 0);
}

STEP2: GridLayoutGroup的布局实现原理上是与HorizontalOrVerticalLayoutGroup相同的,依靠SetInsetAndSizeFromParentEdge方法实现子物体尺寸与位置的设置。

//执行两条轴的布局
public override void SetLayoutHorizontal()
{
    SetCellsAlongAxis(0);
}

public override void SetLayoutVertical()
{
    SetCellsAlongAxis(1);
}

这里为了保持阅读舒适度就不贴太长的代码块了,根据设置的参数计算出startOffset(初始位置),cellSize+spacing(尺寸+间隔) 对子物体进行设置。

for (int i = 0; i < rectChildren.Count; i++)
{
	...
    SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (cellSize[0] + spacing[0]) * positionX, cellSize[0]);
    SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (cellSize[1] + spacing[1]) * positionY, cellSize[1]);
}

ContentSizeFitter

BaseClass: UIBehaviour

Interface: ILayoutSelfController

Intro:布局系统中尺寸调节组件

ContentSizeFitter,是用于调整组件区域使其自适的组件,一般用于与ScrollRect滑动列表以及纵横布局组件搭配,实现动态数量的滑动列表效果,以及与Text组件一起使用,可以根据文字长短进行区域尺寸的变化。

属性介绍

UGUI源码分析:GridLayoutGroup网格布局组件与ContentSizeFitter尺寸调节组件_第3张图片

  • Horizontal Fit:水平适应(三种类型:非强制、最小尺寸适应、最佳尺寸适应)
  • Vertical Fit:垂直适应

ContentSizeFitter继承了ILayoutSelfController接口(ILayoutController接口的衍生),和LayoutGroup一样被布局系统所处理。而和LayoutGroup不同的地方在于,ContentSizeFitter不改变子物体的大小和位置,而是根据子物体(ILayoutElement)来改变自身的尺寸。

实现过程

STEP1:ContentSizeFitter Enable阶段会设置布局标记(脏标记),来触发Rebuild

protected override void OnEnable()
{
    base.OnEnable();
    SetDirty();
}

protected void SetDirty()
{
	...
    //封装成LayoutRebuilder等待被重建
    LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
}

STEP2:当Canvas触发重建过程,其封装成的LayoutRebuilder执行了Rebuild方法。ContentSizeFitter 自身并没有继承ILayoutElement,所有跳过RebuildCalculateLayoutInputHorizontal/Vertical部分,执行了它的SetLayoutHorizontal/Vertical接口方法。

public virtual void SetLayoutHorizontal()
{
    ...
    //根据轴进行尺寸的改变
    HandleSelfFittingAlongAxis(0);
}
public virtual void SetLayoutVertical()
{
    HandleSelfFittingAlongAxis(1);
}
private void HandleSelfFittingAlongAxis(int axis)
{
    //获取目标轴的适应类型
    FitMode fitting = (axis == 0 ? horizontalFit : verticalFit);
    //不强制的类型时不会进行尺寸改变
    if (fitting == FitMode.Unconstrained)
    {
        m_Tracker.Add(this, rectTransform, DrivenTransformProperties.None);
        return;
    }
    //添加Tracker的部分无法被修改
    m_Tracker.Add(this, rectTransform, (axis == 0 ? DrivenTransformProperties.SizeDeltaX : DrivenTransformProperties.SizeDeltaY));

    //根据类型选择适应的尺寸
    if (fitting == FitMode.MinSize)
        rectTransform.SetSizeWithCurrentAnchors((RectTransform.Axis)axis, LayoutUtility.GetMinSize(m_Rect, axis));
    else
        rectTransform.SetSizeWithCurrentAnchors((RectTransform.Axis)axis, LayoutUtility.GetPreferredSize(m_Rect, axis));
}

由上述分析,ContentSizeFitter主要依靠LayoutUtility.GetMinSizeLayoutUtility.GetPreferredSize实现尺寸的变化,这两个方法会从物体自身寻找ILayoutElement,从中获取minWidthpreferredWidth,不存在则返回默认值0
因为ContentSizeFitter自身并未继承ILayoutElement,所以仅仅只有ContentSizeFitter时会将尺寸变为0
如图所示:当垂直适应被选择为Prefered Size时,因为不存在ILayoutElement组件,所以Height被设置成了默认值0
UGUI源码分析:GridLayoutGroup网格布局组件与ContentSizeFitter尺寸调节组件_第4张图片
添加LayoutElement组件,并设置Prefered Size 即可看见变化

UGUI源码分析:GridLayoutGroup网格布局组件与ContentSizeFitter尺寸调节组件_第5张图片


.
.
.
.
.


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


你可能感兴趣的:(UGUI源码分析)