Unity 在子物体固定宽度的情况下父物体高度自适应

最近遇到一个需求 ScrollView中的子物体y是不一样的

自己写了一个丑陋的脚本 这里记录一下

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


public class ContentFitter : MonoBehaviour
{
    public RectTransform selfRect;

    public float top;
    public float left;

    public float paddingTop;
    public float paddingBottom;
    public float paddingLeft;
    public float paddingRight;

    //x是固定的
    public float gridX;

    private List<MatchInfo> matchInfos;
    //最后一行当前数量
    private int lastXIndex;
    //当前列数
    private int currentYIndex;
    //每一列的高度
    private List<float> columnY;
    //一行能放多少物体
    private int rowNum;

    void Start()
    {
        if (selfRect == null)
            selfRect = GetComponent<RectTransform>();

        matchInfos = new List<MatchInfo>();
        selfRect.sizeDelta = new Vector2(paddingLeft + paddingRight, paddingTop + paddingBottom);
    }

    //这里暂时只考虑Y轴
    public void AddContent(RectTransform rect)
    {
        rect.SetParent(selfRect, false);

        MatchInfo newMatchInfo = new MatchInfo(rect);
        if (gridX == 0)
            gridX = newMatchInfo.rect.rect.width;

        if (gridX != newMatchInfo.rect.rect.width)
        {
            Debug.LogError("新添加的物体宽度不符合要求");
            return;
        }
        matchInfos.Add(newMatchInfo);
        //计算一行能放多少个物体
        if (rowNum == 0)
        {
            rowNum = (int)((selfRect.rect.width - paddingLeft - paddingRight - rect.rect.width) / (rect.rect.width + left)) + 1;
            if (rowNum <= 0)
                rowNum = 1;
        }
        if (columnY == null)
        {
            columnY = new List<float>(rowNum);
            columnY.AddRange(Enumerable.Repeat(paddingBottom + paddingTop, rowNum));
        }


        lastXIndex += 1;
        if (matchInfos.Count == 1)//添加进来的第一个物体特殊处理
        {
            selfRect.sizeDelta = new Vector2(selfRect.sizeDelta.x, selfRect.sizeDelta.y + rect.sizeDelta.y);
            currentYIndex += 1;
            columnY[0] += newMatchInfo.rect.rect.height;
        }
        else if (currentYIndex == 1 && lastXIndex <= rowNum)//第一行特殊处理
        {
            //添加对应列高度 并比对是否需要扩容
            columnY[lastXIndex - 1] += newMatchInfo.rect.rect.height;
            for (int i = 0; i < lastXIndex; i++)
            {
                if (columnY[i] > selfRect.rect.height)
                {
                    selfRect.sizeDelta = new Vector2(selfRect.sizeDelta.x, selfRect.sizeDelta.y + columnY[i] - selfRect.rect.height);
                }
            }
        }
        else if (lastXIndex <= rowNum)
        {
            //添加对应列高度 并比对是否需要扩容
            columnY[lastXIndex - 1] += newMatchInfo.rect.rect.height + top;
            for (int i = 0; i < lastXIndex; i++)
            {
                if (columnY[i] > selfRect.rect.height)
                {
                    selfRect.sizeDelta = new Vector2(selfRect.sizeDelta.x, selfRect.sizeDelta.y + columnY[i] - selfRect.rect.height);
                }
            }
        }
        else//换行了 但不是第一行
        {
            lastXIndex = 1;
            currentYIndex += 1;
            columnY[0] += newMatchInfo.rect.rect.height + top;
            if (columnY[0] > selfRect.rect.height)
                selfRect.sizeDelta = new Vector2(selfRect.sizeDelta.x, selfRect.sizeDelta.y + columnY[0] - selfRect.rect.height);
        }

        newMatchInfo.yIndex = currentYIndex;
        newMatchInfo.xIndex = lastXIndex;

        //第一行的sizeDelta
        if (currentYIndex == 1)
        {
            if (lastXIndex == 1)
            {
                newMatchInfo.sizeDelta = new Vector2(paddingLeft + newMatchInfo.rect.rect.width * newMatchInfo.rect.pivot.x, paddingTop + newMatchInfo.rect.rect.height * (1 - newMatchInfo.rect.pivot.y));
            }
            else
            {
                MatchInfo previous = matchInfos[matchInfos.Count - 2];
                newMatchInfo.sizeDelta = new Vector2(previous.sizeDelta.x + left + previous.rect.rect.width * (1 - previous.rect.pivot.x) + newMatchInfo.rect.rect.width * newMatchInfo.rect.pivot.x, paddingTop + newMatchInfo.rect.rect.height * (1 - newMatchInfo.rect.pivot.y));
            }
        }
        //根据上一行的sizeDelta进行计算
        else
        {
            int previousIndex = matchInfos.Count - rowNum - 1;
            newMatchInfo.sizeDelta = matchInfos[previousIndex].sizeDelta + new Vector2(0, matchInfos[previousIndex].rect.rect.height * matchInfos[previousIndex].rect.pivot.y + newMatchInfo.rect.rect.height * (1 - newMatchInfo.rect.pivot.y) + top);
        }
        //获取需要的数据
        //上半部分的值
        float topPosition = selfRect.rect.height * (1 - selfRect.pivot.y) - paddingTop;
        //左半部分的值
        float leftPosition = selfRect.rect.width * selfRect.pivot.x - paddingLeft;

        //计算所有物体的新localPosition
        for (int i = 0; i < matchInfos.Count; i++)
        {
            //更新LocalPostion 根据父物体的pivot计算
            matchInfos[i].rect.localPosition = new Vector2(matchInfos[i].sizeDelta.x - leftPosition, topPosition - matchInfos[i].sizeDelta.y);
        }

    }
    public void DeletedContent(RectTransform rect)
    {
        MatchInfo matchRect = null;
        int matchIndex = -1;
        for (int i = 0; i < matchInfos.Count; i++)
        {
            if (matchInfos[i].rect == rect)
            {
                matchRect = matchInfos[i];
                matchIndex = i;
                break;
            }
        }
        if (matchRect == null)
            return;

        //移除需要删除的物体
        columnY[matchRect.xIndex - 1] -= matchRect.rect.rect.height + top;
        DestroyImmediate(matchInfos[matchIndex].rect.gameObject);
        matchInfos.RemoveAt(matchIndex);

        //更改删除后所在的列
        for (int i = matchIndex; i < matchInfos.Count; i++)
        {
            MatchInfo cur = matchInfos[i];
            if (cur.yIndex == 1)//说明没有上一个物体
            {
                //更新父物体每列高度
                columnY[cur.xIndex - 2] += cur.rect.rect.height;
                columnY[cur.xIndex - 1] -= cur.rect.rect.height;

                cur.xIndex = cur.xIndex - 1;

                float xDelta = 0;
                float yDelta = paddingTop + cur.rect.rect.height * (1 - cur.rect.pivot.y);
                if (cur.xIndex == 1)
                    xDelta = paddingLeft + (cur.xIndex - 1) * gridX + cur.rect.rect.width * cur.rect.pivot.x;
                else
                    xDelta = paddingLeft + (cur.xIndex - 1) * gridX + cur.rect.rect.width * cur.rect.pivot.x + left;

                cur.sizeDelta = new Vector2(xDelta, yDelta);
            }
            else if (cur.yIndex == 2 && cur.xIndex == 1)//说明没有上一个物体 并且会多删除一个top
            {
                //更新父物体每列高度
                columnY[rowNum - 1] += cur.rect.rect.height;
                columnY[cur.xIndex - 1] -= cur.rect.rect.height + top;

                cur.xIndex = rowNum;
                cur.yIndex -= 1;

                float xDelta = 0;
                float yDelta = paddingTop + cur.rect.rect.height * (1 - cur.rect.pivot.y);
                if (cur.xIndex == 1)
                    xDelta = paddingLeft + (cur.xIndex - 1) * gridX + cur.rect.rect.width * cur.rect.pivot.x;
                else
                    xDelta = paddingLeft + (cur.xIndex - 1) * gridX + cur.rect.rect.width * cur.rect.pivot.x + left;
                cur.sizeDelta = new Vector2(xDelta, yDelta);
            }
            else//说明有上一个物体
            {
                if (cur.xIndex == 1)//说明是换行的
                {
                    columnY[rowNum - 1] += cur.rect.rect.height + top;
                    columnY[cur.xIndex - 1] -= cur.rect.rect.height + top;
                    cur.xIndex = rowNum;
                    cur.yIndex -= 1;
                }
                else
                {
                    columnY[cur.xIndex - 2] += cur.rect.rect.height + top;
                    columnY[cur.xIndex - 1] -= cur.rect.rect.height + top;
                    cur.xIndex -= 1;
                }
                //减3的意义 找前一个物体的上一个物体减1 xIndex减1 List链表索引再减1
                MatchInfo refer = matchInfos[(cur.yIndex - 1) * rowNum + cur.xIndex - 3];
                //根据参考信息获取sizeDelta
                float xDelta = refer.sizeDelta.x - refer.rect.rect.width * refer.rect.pivot.x + cur.rect.rect.width * cur.rect.pivot.x;
                float yDelta = refer.sizeDelta.y + refer.rect.rect.height * refer.rect.pivot.y + cur.rect.rect.height * (1 - cur.rect.pivot.y) + top;
                cur.sizeDelta = new Vector2(xDelta, yDelta);
            }
        }

        //更新父物体
        float maxHeight = 0;
        int maxIndex = 0;
        for (int i = 0; i < columnY.Count; i++)
        {
            if (columnY[i] > maxHeight)
            {
                maxHeight = columnY[i];
                maxIndex = i;
            }
        }
        //说明需要改变父物体了
        if (selfRect.rect.height != maxHeight)
        {
            float delta = selfRect.rect.height - maxHeight;
            selfRect.sizeDelta = new Vector2(selfRect.sizeDelta.x, selfRect.sizeDelta.y - delta);
        }
        //更新所有物体的localPosition

        //上半部分的值
        float topPosition = selfRect.rect.height * (1 - selfRect.pivot.y) - paddingTop;
        //左半部分的值
        float leftPosition = selfRect.rect.width * selfRect.pivot.x - paddingLeft;
        for (int i = 0; i < matchInfos.Count; i++)
        {
            //更新LocalPostion 根据父物体的pivot计算
            matchInfos[i].rect.localPosition = new Vector2(matchInfos[i].sizeDelta.x - leftPosition, topPosition - matchInfos[i].sizeDelta.y);
        }
        //更新最后一行数量和总行数
        lastXIndex -= 1;
        if (lastXIndex == 0)
        {
            currentYIndex -= 1;
            if (currentYIndex == 0)
                lastXIndex = 0;
            else
                lastXIndex = rowNum;
        }
    }

    private class MatchInfo
    {
        public RectTransform rect;
        //相对于父物体上框距离子物体上框和左框的距离
        public Vector2 sizeDelta;
        //是第几行
        public int yIndex;
        //是第几列
        public int xIndex;

        public MatchInfo(RectTransform rect)
        {
            this.rect = rect;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值