Как сделать так, чтобы пользовательская Unity LayoutGroup расширялась, чтобы соответствовать содержимому
Я пытаюсь использовать пользовательскую FlowLayoutGroup, как описано в ответах на этот вопрос (также на GitHub), в ситуации, когда необходимо изменить размер по вертикали, чтобы содержать его дочерние элементы.
Моя установка выглядит так:
- ScrollableRect
- Панель с компоновкой VerticalLayoutGroup (содержимое родительской прокрутки), размер которой должен изменяться по вертикали, чтобы соответствовать дочерним элементам:
- Панель с FlowLayoutGroup, размер которой должен изменяться по вертикали, чтобы соответствовать детям
- Панель с FlowLayoutGroup (2) также должна изменить размер...
- так далее...
- Панель с компоновкой VerticalLayoutGroup (содержимое родительской прокрутки), размер которой должен изменяться по вертикали, чтобы соответствовать дочерним элементам:
Я добавил приспособитель размера контента в FlowLayoutGroup, подправил дочерние элементы управления размера макета вертикальной группы, но безуспешно.
Пользователь может добавлять и удалять дочерние группы во время работы приложения, и я хочу, чтобы пользовательский интерфейс отвечал, поэтому невозможно заранее установить высоту всего.
Я также посмотрел в исходном коде Unity, чтобы попытаться выяснить, как записать это в компонент самостоятельно. Это лучший вариант, но у меня уходит немало времени, так как я новичок в Unity и C#. Надеюсь, кто-то уже решил подобную проблему.
Все работает так, как нужно / ожидается, за исключением отсутствующего поведения LayoutGroups, изменяющего размер, чтобы соответствовать их дочерним элементам по вертикали.
Как я могу это сделать?
1 ответ
После некоторого времени и значка с перекрученной травой я решил выделить время, чтобы найти решение, надеюсь, кому-то еще это будет полезно.
Опять же, это модифицированная версия работы, проделанной здесь. Спасибо за это. Этот компонент теперь вычисляет его собственный предпочтительный размер.
Основные изменения:
- Я отодрал это довольно строго:
- Все горизонтальные переопределения очищены, мне нужно только поведение горизонтального переноса
- Удалены некоторые видимые переменные похмелья из класса GridLayout
- Логика для вычисления дочерних позиций и, в свою очередь, количества строк, предпочтительная высота в своем собственном методе.
- Дочерние позиции хранятся в массиве 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;
}
}