总所周知 Grid Layout Group 平平无奇,单元格大小都要手动设置。
想做个九宫格布局。还要手动计算单元格大小,偏移量等
使用Vertical Layout Group 和 Horzontal Layout Group 组合又非常麻烦
想要自定义布局,只需继承 Layout Group 重写 CalculateLayoutInputHorizontal 单元格坐标以及大小即可
public override void CalculateLayoutInputHorizontal()
{
base.CalculateLayoutInputHorizontal();
//code
}
Layout Group定义自行查阅文档:
https://docs.unity3d.com/2018.4/Documentation/ScriptReference/UI.LayoutGroup.html
第一步:对子对象数量开平方根计算出行列数
float squareRoot = Mathf.Sqrt(rectChildren.Count);
var rows = Mathf.CeilToInt(squareRoot);
var columns = Mathf.CeilToInt(squareRoot);
//如果行或列为0没必要继续
if (rows == 0 || columns == 0)
return;
第二步:计算单元格大小
var cellWidth = rectTransform.rect.width / columns;
var cellHeight = rectTransform.rect.height / rows;
第三步:计算单元格坐标并设置单元格
for (var i = 0; i < rectChildren.Count; i++)
{
//获取行索引
var rowIndex = i / columns;
//获取列索引
var columnIndex = i % columns;
//计算坐标
var x = cellWidth * columnIndex ;
var y = cellHeight * rowIndex ;
var item = rectChildren[i];
//设置单元格坐标以及大小
SetChildAlongAxis(item, 0, x, cellWidth);
SetChildAlongAxis(item, 1, y, cellHeight);
}
现在你将会得到一个这样的布局
单元格之间应该有间隔存在,添加间隔的大小计算
var spacingWidth = spacing.x / columns * (columns - 1);
var spacingHeight = spacing.y / rows * (rows - 1);
单元格大小减去间隔大小
var cellWidth = width - spacingWidth;
var cellHeight = height - spacingHeight;
单元格坐标添加上间隔位置
var x = (cellWidth + spacing.x) * columnIndex;
var y = (cellHeight + spacing.y) * rowIndex;
这是添加上间隔计算后的布局样子
除了间隔,还应该添加上内边距的偏移,计算内边距大小
var paddingWidth = (padding.left + padding.right) / columns;
var paddingHeight = (padding.top + padding.bottom) / rows;
单元格减少内边距大小
var cellWidth = width - spacingWidth - paddingWidth;
var cellHeight = height - spacingHeight - paddingHeight;
单元格坐标偏移
var x = (cellWidth + spacing.x) * columnIndex + padding.left;
var y = (cellHeight + spacing.y) * rowIndex + padding.top;
最终效果:
除了平铺,你可能还想要它基于水平或垂直布局
if (fitType == FitType.FIXEDCOLUMNS)
columns = 1;
else if (fitType == FitType.FIXEDROWS)
rows = 1;
if (fitType == FitType.Width || fitType == FitType.FIXEDCOLUMNS)
rows = Mathf.CeilToInt(rectChildren.Count / (float)columns);
if (fitType == FitType.Height || fitType == FitType.FIXEDROWS)
columns = Mathf.CeilToInt(rectChildren.Count / (float)rows);
总结:
因为对象是可以嵌套的,所以可以很方便的做出类似网站一样的布局
实现代码:
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 灵活的网格布局
/// </summary>
public class FlexibleGridLayout : LayoutGroup
{
/// <summary>
/// 适配类型
/// </summary>
public enum FitType
{
/// <summary>
/// 均匀分布
/// </summary>
UniForm,
/// <summary>
/// 水平优先
/// </summary>
Horizontal,
/// <summary>
/// 垂直优先
/// </summary>
Verticality,
/// <summary>
/// 固定行
/// </summary>
FixedRow,
/// <summary>
/// 固定列
/// </summary>
FixedColumn
}
/// <summary>
/// 适配类型,默认均匀分布
/// </summary>
[Header("Flexible Grid")]
public FitType fitType = FitType.UniForm;
/// <summary>
/// 单元格间隔
/// </summary>
public Vector2 spacing;
/// <summary>
/// 计算水平布局
/// </summary>
public override void CalculateLayoutInputHorizontal()
{
base.CalculateLayoutInputHorizontal();
float squareRoot = Mathf.Sqrt(rectChildren.Count);
var rows = Mathf.CeilToInt(squareRoot);
var columns = Mathf.CeilToInt(squareRoot);
if (fitType == FitType.FixedColumn)
columns = 1;
else if (fitType == FitType.FixedRow)
rows = 1;
if (fitType == FitType.Horizontal || fitType == FitType.FixedColumn)
rows = Mathf.CeilToInt(rectChildren.Count / (float)columns);
if (fitType == FitType.Verticality || fitType == FitType.FixedRow)
columns = Mathf.CeilToInt(rectChildren.Count / (float)rows);
if (rows == 0 || columns == 0)
return;
var width = rectTransform.rect.width / columns;
var height = rectTransform.rect.height / rows;
var spacingWidth = spacing.x / columns * (columns - 1);
var spacingHeight = spacing.y / rows * (rows - 1);
var paddingWidth = (padding.left + padding.right) / columns;
var paddingHeight = (padding.top + padding.bottom) / rows;
var cellWidth = width - spacingWidth - paddingWidth;
var cellHeight = height - spacingHeight - paddingHeight;
for (var i = 0; i < rectChildren.Count; i++)
{
var rowIndex = i / columns;
var columnIndex = i % columns;
var x = (cellWidth + spacing.x) * columnIndex + padding.left;
var y = (cellHeight + spacing.y) * rowIndex + padding.top;
var item = rectChildren[i];
SetChildAlongAxis(item, 0, x, cellWidth);
SetChildAlongAxis(item, 1, y, cellHeight);
}
}
public override void CalculateLayoutInputVertical()
{
}
public override void SetLayoutHorizontal()
{
//布局刷新是从最里面开始一直到外面
//有时候外面布局会无效
//在这里再从外面到里面刷新一次
CalculateLayoutInputHorizontal();
}
public override void SetLayoutVertical()
{
}
}