当前位置: 首页 > article >正文

Unity 重写GridLayoutGroup使居中对齐

        使用GridLayoutGroup的时候,以StartAxis设置为Horizontal为例,当我们设置ChildAlignment为***Center时,我们会发现,如果显示内容只有一行,那么元素排列是居中的,但是如果显示内容不止一行,那么最后一行的元素排列是靠左或者靠右(由StartCornner决定)。这就导致假设有4个子物体,固定为3列,第2列只有一个子物体,但是不是居中的,是靠左/右的,不符合美术的要求。

        要解决这个问题有一种方案是把GridLayout分割成HorizontalLayout和VerticalLayout,然后分行/列显示,这样保证每一行/列都居中对齐,但是有点麻烦的是需要对数据进行分割,相对来讲比较麻烦。

        另一种方案就是重写GridLayoutGroup,实现居中的需求。通过阅读源码+ 断点数据,发现逻辑还是比较好理解的。以StartAxis设置为Horizontal为例 ,原逻辑是通过CellSize与Spacing计算一行所占的长度与Layout对象本身的size做对比,然后再根据设置的布局方式计算出坐标偏移值,以此来确定每个子物体的坐标,出现上述的问题是因为,计算行所占的长度时,只计算了一次最大长度,这是不合理的,因为当最后一行没有填充满的时候,所占的长度与上面的行是不一样的,以此为基础修改源码。代码如下,修改内容从116行开始:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace UnityEngine.UI
{
    public class GridLayoutGroupCenter : GridLayoutGroup
    {
        /// <summary>
        /// Called by the layout system
        /// Also see ILayoutElement
        /// </summary>
        public override void SetLayoutHorizontal()
        {
            SetCellsAlongAxis(0);
        }

        /// <summary>
        /// Called by the layout system
        /// Also see ILayoutElement
        /// </summary>
        public override void SetLayoutVertical()
        {
            SetCellsAlongAxis(1);
        }

        private void SetCellsAlongAxis(int axis)
        {
            // Normally a Layout Controller should only set horizontal values when invoked for the horizontal axis
            // and only vertical values when invoked for the vertical axis.
            // However, in this case we set both the horizontal and vertical position when invoked for the vertical axis.
            // Since we only set the horizontal position and not the size, it shouldn't affect children's layout,
            // and thus shouldn't break the rule that all horizontal layout must be calculated before all vertical layout.
            var rectChildrenCount = rectChildren.Count;
            if (axis == 0)
            {
                // Only set the sizes when invoked for horizontal axis, not the positions.

                for (int i = 0; i < rectChildrenCount; i++)
                {
                    RectTransform rect = rectChildren[i];

                    m_Tracker.Add(this, rect,
                        DrivenTransformProperties.Anchors |
                        DrivenTransformProperties.AnchoredPosition |
                        DrivenTransformProperties.SizeDelta);

                    rect.anchorMin = Vector2.up;
                    rect.anchorMax = Vector2.up;
                    rect.sizeDelta = cellSize;
                }
                return;
            }

            float width = rectTransform.rect.size.x;
            float height = rectTransform.rect.size.y;

            int cellCountX = 1;
            int cellCountY = 1;
            if (m_Constraint == Constraint.FixedColumnCount)
            {
                cellCountX = m_ConstraintCount;

                if (rectChildrenCount > cellCountX)
                    cellCountY = rectChildrenCount / cellCountX + (rectChildrenCount % cellCountX > 0 ? 1 : 0);
            }
            else if (m_Constraint == Constraint.FixedRowCount)
            {
                cellCountY = m_ConstraintCount;

                if (rectChildrenCount > cellCountY)
                    cellCountX = rectChildrenCount / cellCountY + (rectChildrenCount % cellCountY > 0 ? 1 : 0);
            }
            else
            {
                if (cellSize.x + spacing.x <= 0)
                    cellCountX = int.MaxValue;
                else
                    cellCountX = Mathf.Max(1, Mathf.FloorToInt((width - padding.horizontal + spacing.x + 0.001f) / (cellSize.x + spacing.x)));

                if (cellSize.y + spacing.y <= 0)
                    cellCountY = int.MaxValue;
                else
                    cellCountY = Mathf.Max(1, Mathf.FloorToInt((height - padding.vertical + spacing.y + 0.001f) / (cellSize.y + spacing.y)));
            }


            //startCorner = 0
            int cornerX = (int)startCorner % 2;//0
            int cornerY = (int)startCorner / 2;//0

            int cellsPerMainAxis, actualCellCountX, actualCellCountY;
            if (startAxis == Axis.Horizontal)
            {
                cellsPerMainAxis = cellCountX;
                actualCellCountX = Mathf.Clamp(cellCountX, 1, rectChildrenCount);
                actualCellCountY = Mathf.Clamp(cellCountY, 1, Mathf.CeilToInt(rectChildrenCount / (float)cellsPerMainAxis));
            }
            else
            {
                cellsPerMainAxis = cellCountY;
                actualCellCountY = Mathf.Clamp(cellCountY, 1, rectChildrenCount);
                actualCellCountX = Mathf.Clamp(cellCountX, 1, Mathf.CeilToInt(rectChildrenCount / (float)cellsPerMainAxis));
            }

            //总长度 & 总高度
            Vector2 requiredSpace = new Vector2(
                actualCellCountX * cellSize.x + (actualCellCountX - 1) * spacing.x,
                actualCellCountY * cellSize.y + (actualCellCountY - 1) * spacing.y
            );
            Vector2 startOffset = new Vector2(
                GetStartOffset(0, requiredSpace.x),
                GetStartOffset(1, requiredSpace.y)
            );

            //最后一行/列的偏移参数
            Vector2 requiredSpaceNew = Vector2.zero;
            Vector2 startOffsetNew = Vector2.zero;
            //水平顺序布局,计算是否为整行
            if (startAxis == Axis.Horizontal)
            {
                //少于一行,按照原数据计算
                if (rectChildrenCount <= actualCellCountX)
                {
                    requiredSpaceNew = requiredSpace;
                    startOffsetNew = startOffset;
                }
                else
                {
                    //整行
                    var rem = rectChildrenCount % actualCellCountX;
                    if (rem == 0)
                    {
                        requiredSpaceNew = requiredSpace;
                        startOffsetNew = startOffset;
                    }
                    else
                    {
                        requiredSpaceNew = new Vector2(
                            rem * cellSize.x + (rem - 1) * spacing.x,
                            actualCellCountY * cellSize.y + (actualCellCountY - 1) * spacing.y);
                        startOffsetNew = new Vector2(
                            GetStartOffset(0, requiredSpaceNew.x),
                            GetStartOffset(1, requiredSpaceNew.y)
                        );
                    }
                }
                
            }
            else
            {
                //少于一列,按照原数据计算
                if (rectChildrenCount <= actualCellCountY)
                {
                    requiredSpaceNew = requiredSpace;
                    startOffsetNew = startOffset;
                }
                else
                {
                    //整列
                    var rem = rectChildrenCount % actualCellCountY;
                    if (rem == 0)
                    {
                        requiredSpaceNew = requiredSpace;
                        startOffsetNew = startOffset;
                    }
                    else
                    {
                        requiredSpaceNew = new Vector2(
                            actualCellCountX * cellSize.x + (actualCellCountX - 1) * spacing.x,
                            rem * cellSize.y + (rem - 1) * spacing.y);
                        startOffsetNew = new Vector2(
                            GetStartOffset(0, requiredSpaceNew.x),
                            GetStartOffset(1, requiredSpaceNew.y)
                        );
                    }
                }
            }

            //最后一行/列的起始索引
            int lastRowIndex = rectChildrenCount - rectChildrenCount % actualCellCountX;
            int lastColIndex = rectChildrenCount - rectChildrenCount % actualCellCountY;


            for (int i = 0; i < rectChildrenCount; i++)
            {
                int positionX;
                int positionY;
                //最后一行/列
                bool lastRowOrCol = false;
                if (startAxis == Axis.Horizontal)
                {
                    positionX = i % cellsPerMainAxis;
                    positionY = i / cellsPerMainAxis;

                    lastRowOrCol = i >= lastRowIndex;
                }
                else
                {
                    positionX = i / cellsPerMainAxis;
                    positionY = i % cellsPerMainAxis;

                    lastRowOrCol = i >= lastColIndex;

                }

                if (cornerX == 1)
                    positionX = actualCellCountX - 1 - positionX;
                if (cornerY == 1)
                    positionY = actualCellCountY - 1 - positionY;

                if (lastRowOrCol)
                {
                    SetChildAlongAxis(rectChildren[i], 0, startOffsetNew.x + (cellSize[0] + spacing[0]) * positionX, cellSize[0]);
                    SetChildAlongAxis(rectChildren[i], 1, startOffsetNew.y + (cellSize[1] + spacing[1]) * positionY, cellSize[1]);
                }
                else
                {
                    SetChildAlongAxis(rectChildren[i], 0, startOffset.x + (cellSize[0] + spacing[0]) * positionX, cellSize[0]);
                    SetChildAlongAxis(rectChildren[i], 1, startOffset.y + (cellSize[1] + spacing[1]) * positionY, cellSize[1]);
                }

            }
        }
    }
}

        实际要添加的逻辑很少,代码中加了比较多的ifelse是为了减少运算量,因为部分情况下,是不需要计算新的offset的。


http://www.kler.cn/a/451959.html

相关文章:

  • 在MySQL 主库上进行自动清理 purged gtid 时,会等待 binlog复制到从库吗
  • 谈谈JSON
  • 48页PPT|2024智慧仓储解决方案解读
  • 在vscode的ESP-IDF中使用自定义组件
  • 嵌入式设备常用性能和内存调试指令
  • 计算机基础知识复习12.24
  • HarmonyOS NEXT 实战之元服务:静态案例效果---最近播放音乐
  • imx6ull qt多页面控制系统(正点原子imx系列驱动开发)
  • ASN.1 轻松入门2
  • HarmonyOS NEXT 实战之元服务:静态案例效果(二)
  • 131、sqlserver中使用mybatis中的Page进行分页查询时,SQL成功执行(控制台已打印),Page的Records没值bug1.代码复现:
  • NUCLEO-F446RE测试板验证DS100示波器功能
  • 【视觉惯性SLAM:编译及编译工具】
  • 2024.8 设计可解释的 ML 系统以增强对医疗保健的信任:对提出的负责任的临床医生-AI 协作框架的系统评价
  • wordpress调用指定ID分类下浏览最多的内容
  • 印度软件业的发展能给中国软件行业什么样的启示和借鉴
  • C语言-基因序列转换独热码(one-hot code)
  • 开关电源特点、分类、工作方式
  • 【开源免费】基于SpringBoot+Vue.JS在线宠物用品交易网站(JAVA毕业设计)
  • 网络下载ts流媒体
  • JVM简介—1.Java内存区域
  • VBA实现遍历Excel文件将指定的单元格内容拷贝到当前工作簿
  • whisper.cpp: PC端测试 -- 电脑端部署音频大模型
  • 图像处理-Ch6-彩色图像处理
  • 修改输出资源的名称和路径、自动清空上次打包资源
  • 【C 语言】内存节省机制