本文共 6724 字,大约阅读时间需要 22 分钟。
最近在工作中遇到了UGUI文图混合排列的问题,查阅相应资料后发现添加Horizontal Layout Group组件和Content Size Fitter组件就可以实现自动居中的效果。想半天没想明白是什么原理,于是打算深入探究这两个组件的魔力。
先看Horizontal Layout Group组件。
这是Horizontal Layout Group组件的官方源码。
namespace UnityEngine.UI{ [AddComponentMenu("Layout/Horizontal Layout Group", 150)] ////// Layout class for arranging child elements side by side. /// public class HorizontalLayoutGroup : HorizontalOrVerticalLayoutGroup { protected HorizontalLayoutGroup() { } ////// Called by the layout system. Also see ILayoutElement /// public override void CalculateLayoutInputHorizontal() { base.CalculateLayoutInputHorizontal(); CalcAlongAxis(0, false); } ////// Called by the layout system. Also see ILayoutElement /// public override void CalculateLayoutInputVertical() { CalcAlongAxis(1, false); } ////// Called by the layout system. Also see ILayoutElement /// public override void SetLayoutHorizontal() { SetChildrenAlongAxis(0, false); } ////// Called by the layout system. Also see ILayoutElement /// public override void SetLayoutVertical() { SetChildrenAlongAxis(1, false); } }}
我们可以看到Horizontal Layout Group继承的是HorizontalOrVerticalLayoutGroup,而HorizontalOrVerticalLayoutGroup继承的又是LayoutGroup。因此Horizontal Layout Group具有所有LayoutGroup的通用特性,和Vertical Layout Group、Grid Layout Group十分相似。
Horizontal Layout Group中有几个基本属性,如Padding、Spacing、Child Alignment、Child Force Expand,新版本还添加了两个新属性。实际上就是边界留空。它包括上下左右四个边界,分别可以调整其子物体内容与边界之间的距离。
实际上指的是其子物体之间的距离。
即子物体的对其方式,有上左、上中、上右、中左、中中、中右、下左、下中、下右。
即子物体是否需要通过拉伸来强制填充满父物体。可以选择宽填充,也可以选择高填充,还可以都选或都不选。
Horizontal Layout Group之所以能够自动排布子物体,关键就在于它本质是个Layout Group。也就是说对于它所有的子物体都会进行等距离排布。因此子物体是Text还是Image都可以很工整地处理。再看Content Size Fitter组件。
这是Content Size Fitter组件的官方源码。
using UnityEngine.EventSystems;namespace UnityEngine.UI{ [AddComponentMenu("Layout/Content Size Fitter", 141)] [ExecuteAlways] [RequireComponent(typeof(RectTransform))] ////// Resizes a RectTransform to fit the size of its content. /// ////// The ContentSizeFitter can be used on GameObjects that have one or more ILayoutElement components, such as Text, Image, HorizontalLayoutGroup, VerticalLayoutGroup, and GridLayoutGroup. /// public class ContentSizeFitter : UIBehaviour, ILayoutSelfController { ////// The size fit modes avaliable to use. /// public enum FitMode { ////// Don't perform any resizing. /// Unconstrained, ////// Resize to the minimum size of the content. /// MinSize, ////// Resize to the preferred size of the content. /// PreferredSize } [SerializeField] protected FitMode m_HorizontalFit = FitMode.Unconstrained; ////// The fit mode to use to determine the width. /// public FitMode horizontalFit { get { return m_HorizontalFit; } set { if (SetPropertyUtility.SetStruct(ref m_HorizontalFit, value)) SetDirty(); } } [SerializeField] protected FitMode m_VerticalFit = FitMode.Unconstrained; ////// The fit mode to use to determine the height. /// public FitMode verticalFit { get { return m_VerticalFit; } set { if (SetPropertyUtility.SetStruct(ref m_VerticalFit, value)) SetDirty(); } } [System.NonSerialized] private RectTransform m_Rect; private RectTransform rectTransform { get { if (m_Rect == null) m_Rect = GetComponent(); return m_Rect; } } private DrivenRectTransformTracker m_Tracker; protected ContentSizeFitter() { } protected override void OnEnable() { base.OnEnable(); SetDirty(); } protected override void OnDisable() { m_Tracker.Clear(); LayoutRebuilder.MarkLayoutForRebuild(rectTransform); base.OnDisable(); } protected override void OnRectTransformDimensionsChange() { SetDirty(); } private void HandleSelfFittingAlongAxis(int axis) { FitMode fitting = (axis == 0 ? horizontalFit : verticalFit); if (fitting == FitMode.Unconstrained) { // Keep a reference to the tracked transform, but don't control its properties: m_Tracker.Add(this, rectTransform, DrivenTransformProperties.None); return; } m_Tracker.Add(this, rectTransform, (axis == 0 ? DrivenTransformProperties.SizeDeltaX : DrivenTransformProperties.SizeDeltaY)); // Set size to min or preferred size 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)); } /// /// Calculate and apply the horizontal component of the size to the RectTransform /// public virtual void SetLayoutHorizontal() { m_Tracker.Clear(); HandleSelfFittingAlongAxis(0); } ////// Calculate and apply the vertical component of the size to the RectTransform /// public virtual void SetLayoutVertical() { HandleSelfFittingAlongAxis(1); } protected void SetDirty() { if (!IsActive()) return; LayoutRebuilder.MarkLayoutForRebuild(rectTransform); } #if UNITY_EDITOR protected override void OnValidate() { SetDirty(); } #endif }}
Content Size Fitter从本质上来说就是可以自动根据物体内容来自动适配物体的大小。
可以调整水平适配和垂直适配,分别有三个调整选项,Unconstrainted、MinSize和PreferredSize。
是指物体不根据布局和内容来调整大小,因此需要手动来进行调整。
是指物体根据布局的最小值来调整大小。
是指物体根据内容来调整大小。
总的来说,这个组件经常与任意一种Layout Group组件一起使用,因为无论添加多少子物体都可以自动对父物体进行布局和大小控制。特别方便,特别实用。UGUI布局问题解决方式其实有很多种,Unity自带的UI组件有很多,这里列出的方法仅仅只是其中一种,也是比较常见的一种。所以一定要不断的学习,遇到问题的时候一定要去挖它的底层原理,只有知道底层才算是真正的知道问题。
因为本人也还在学习过程中,所以肯定有很多地方考虑不全,以及会有很多错误。希望有大佬看到能够及时指出,我会立刻进行改正,帮助我自己和大家共同进步!
有问题的小伙伴们可以在下方留言,大家可以一起讨论。
转载地址:http://hkxcz.baihongyu.com/