如何使自定义Unity LayoutGroup扩展以适合内容

时间:2019-01-16 23:01:53

标签: c# unity3d unity3d-2dtools unity3d-ui

在需要垂直调整大小以容纳其子级的情况下,我尝试使用this question(也在GitHub的答案中)所述的自定义FlowLayoutGroup。

我的设置如下:

  • ScrollableRect
    • 具有VerticalLayoutGroup comp(父scrollrect的内容)的面板,应垂直调整大小以适合子代:
      • 具有FlowLayoutGroup的面板,该面板应垂直调整大小以适合孩子
      • 具有FlowLayoutGroup(2)的面板还必须调整大小...
      • 等...

我向FlowLayoutGroup添加了一个内容大小调整器,调整了垂直组的布局子尺寸控制,但是没有成功。

用户可以在应用程序运行时添加和删除组的子级,并且我希望UI能够响应,因此无法预先设置所有项目的高度。

我还查看了统一的源代码,以尝试弄清楚如何将其自己写入组件。这看起来是最好的选择,但是由于我是Unity和C#的新手,所以花了我很多时间。希望有人已经解决了类似的问题。

除LayoutGroups调整大小以垂直适合其子代的缺失行为外,所有功能均符合期望/预期。

我该怎么做?

1 个答案:

答案 0 :(得分:0)

经过一段时间和风滚草徽章后,我决定花点时间制定解决方案,希望其他人也能从中受益。

同样,这是the work done here的修改版本。感谢那。现在,此组件将计算自己的首选大小。

主要更改:

  1. 我非常认真地将其剥离:
    • 所有水平替换都被清空,我只需要水平包装行为
    • 从GridLayout类中删除了一些明显的宿醉变量
  2. 用于计算子位置以及行数的逻辑,首选的高度是它自己的方法。
  3. 子位置存储在Vector2数组中,以将计算与子设置分开。

这解决了整个组件的高度不可调的问题,它也立即响应,使用原始脚本,因为设置了孩子rectTransforms的方式,然后访问该脚本花费了两个“周期”来识别孩子的尺寸。

这可以满足我的所有需求,我想它也可以很容易地重新加工以处理垂直包裹...

using UnityEngine;
using UnityEngine.UI;

[AddComponentMenu("Layout/Wrap Layout Group", 153)]
public class WrapLayoutGroup : LayoutGroup
{
    [SerializeField] protected Vector2 m_Spacing = Vector2.zero;
    public Vector2 spacing { get { return m_Spacing; } set { SetProperty(ref m_Spacing, value); } }

    [SerializeField] protected bool m_Horizontal = true;
    public bool horizontal { get { return m_Horizontal; } set { SetProperty(ref m_Horizontal, value); } }

    private float availableWidth { get { return rectTransform.rect.width - padding.horizontal + spacing.x; } }

    private const float MIN_HEIGHT = 80;

    private int preferredRows = 1;
    private float calculatedHeight = MIN_HEIGHT;

    private Vector2[] childPositions = new Vector2[0];

    protected WrapLayoutGroup()
    { }

#if UNITY_EDITOR
    protected override void OnValidate()
    {
        base.OnValidate();
    }

#endif

    public override void CalculateLayoutInputVertical()
    {
        calculatePositionsAndRequiredSize();
        SetLayoutInputForAxis(calculatedHeight, calculatedHeight, -1, 1);
    }

    public override void SetLayoutHorizontal() { }

    public override void SetLayoutVertical()
    {
        SetChildren();
    }

    private void SetChildren()
    {
        for (int i = 0; i < rectChildren.Count; i++)
        {
            RectTransform child = rectChildren[i];
            SetChildAlongAxis(child, 0, childPositions[i].x, LayoutUtility.GetPreferredWidth(child));
            SetChildAlongAxis(child, 1, childPositions[i].y, LayoutUtility.GetPreferredHeight(child));
        }
    }

    private void calculatePositionsAndRequiredSize()
    {
        childPositions = new Vector2[rectChildren.Count];

        Vector2 startOffset = new Vector2(
            GetStartOffset(0, 0),
            GetStartOffset(1, 0)
        );

        Vector2 currentOffset = new Vector2(
            startOffset.x,
            startOffset.y
        );

        float childHeight = 0;
        float childWidth = 0;
        float maxChildHeightInRow = 0;

        int currentRow = 1;

        for (int i = 0; i < rectChildren.Count; i++)
        {
            childHeight = LayoutUtility.GetPreferredHeight(rectChildren[i]);
            childWidth = LayoutUtility.GetPreferredWidth(rectChildren[i]);

            //check for new row start
            if (currentOffset.x + spacing.x + childWidth > availableWidth && i != 0)
            {
                currentOffset.x = startOffset.x;
                currentOffset.y += maxChildHeightInRow + spacing.y;
                currentRow++;
                maxChildHeightInRow = 0;
            }

            childPositions[i] = new Vector2(
                currentOffset.x,
                currentOffset.y
            );

            //update offset
            maxChildHeightInRow = Mathf.Max(maxChildHeightInRow, childHeight);
            currentOffset.x += childWidth + spacing.x;

        }

        //update groups preferred dimensions
        preferredRows = currentRow;
        calculatedHeight = currentOffset.y + maxChildHeightInRow + padding.vertical - spacing.y;
    }
}