How to make custom Unity LayoutGroup expand to fit contents

偶尔善良 提交于 2019-12-13 04:27:44

问题


I am attempting to use a custom FlowLayoutGroup as described in the answers of this question ( also up on GitHub) in a situation where it needs to resize vertically to contain it's children.

My setup looks like this:

  • ScrollableRect
    • Panel with VerticalLayoutGroup comp (content of parent scrollrect) that should resize vertically to fit children:
      • Panel with FlowLayoutGroup that should resize vertically to fit children
      • Panel with FlowLayoutGroup (2) also must resize...
      • etc...

I have added a content size fitter to the FlowLayoutGroup, tweaked the layout child size controls of the vertical group, but with no success.

The user may add and remove children of the groups while the app is running and I want the UI to respond so it is not possible to set the height of everything in advance.

I have also looked in the unity source code to try and figure out how to write this into the component myself. This is looking the best bet but taking me considerable time as I'm new to Unity and C#. Hoping someone has solved a similar problem already.

Everything functions as desired/expected except for the missing behaviour of LayoutGroups resizing to fit their children vertically.

How can I do this?


回答1:


After some time and a tumbleweed badge I've decided to put the time in to make a solution, hopefully someone else benefits too.

Again, this is a modified version of the work done here. Thanks for that. This component now computes it's own preferred size.

Main changes:

  1. I stripped it back quite severely:
    • All horizontal overrides are emptied, I only need the horizontal wrapping behaviour
    • Removed some apparent hangover variables from GridLayout class
  2. Logic to calculate child positions and in turn number of rows, preferred height is in it's own method.
  3. Child positions are stored in an Vector2 array to separate calculation from child setting.

This fixes the problem of height of the entire component not adjusting, it also immediately responds, with the original script because of the way children rectTransforms were set then accessed the script took two 'cycles' to recognize the dimensions of a child.

This suits all my needs, I imagine it can be fairly easily reworked to handle vertical wrap too...

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;
    }
}



来源:https://stackoverflow.com/questions/54226685/how-to-make-custom-unity-layoutgroup-expand-to-fit-contents

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!