【UGUI】自动布局

UGUI自动布局一直适应 GridLayoutGroup,但是,使用grid时有一个很严重的问题就是:当打开Profiler中的Deep Profile时,整个Unity都会崩溃.(目前使用版本v5.6.0)此外,还有一个问题,但是我给忘了...
所以,还是自己动手造轮子吧,代码如下:

using UnityEngine;

/// <summary>
/// Introduction: UILayout, UI自动布局,替代GridLayoutGroup, 子节点改变时需要自行调用 Rebuild
/// Author: 	Garson
/// Time: 
/// </summary>
[RequireComponent(typeof(RectTransform))]
public class UILayout : MonoBehaviour
{
    [SerializeField]//边距
    private RectOffset m_Padding = new RectOffset();
    public RectOffset padding { get { return m_Padding; } set { SetProperty(ref m_Padding, value); } }
    [SerializeField]//单元格大小
    private Vector2 m_CellSize = new Vector2(100, 100);
    public Vector2 cellSize { get { return m_CellSize; } set { SetProperty(ref m_CellSize, value);} }
    [SerializeField]//开始排布位置
    private StartCorner m_StartCorner;
    public StartCorner startCorner { get { return m_StartCorner; } set { SetProperty(ref m_StartCorner, value);} }
    [SerializeField]//排列方式
    private Arrangement m_Arrangement;
    public Arrangement arrangement { get { return m_Arrangement; } set { SetProperty(ref m_Arrangement, value);} }
    [Tooltip("<=0自动计算")]
    [SerializeField]//每行数目,<=0自动计算
    private int m_FixedCount;
    public int fixedCount { get { return m_FixedCount; } set { SetProperty(ref m_FixedCount, value); } }
    [SerializeField]
    private Vector2 m_Space;//行列间距
    public Vector2 space { get { return m_Space; } set { SetProperty(ref m_Space, value); } }
    private Vector2 m_WrapSize;//排布后该组件应该大小
    public Vector2 wrapSize { get { return m_WrapSize; } }

    private RectTransform m_rectTransform;
    private RectTransform rectTransform { get { if(m_rectTransform==null)m_rectTransform=transform as RectTransform;return m_rectTransform;} }

    public void Rebuild()
    {
        RectTransform.Edge edgeX = RectTransform.Edge.Left, edgeY= RectTransform.Edge.Top;
        float paddingX = 0, paddingY = 0, sizeX = 0, sizeY = 0;
        switch (m_StartCorner)
        {
            case StartCorner.TopLeft:
                edgeY = RectTransform.Edge.Top;
                edgeX = RectTransform.Edge.Left;
                paddingX = m_Padding.left;
                paddingY = m_Padding.top;
                sizeX = m_Padding.right;
                sizeY = m_Padding.bottom;
                break;
            case StartCorner.TopRight:
                edgeY = RectTransform.Edge.Top;
                edgeX = RectTransform.Edge.Right;                  
                paddingX = m_Padding.right;
                paddingY = m_Padding.top;
                sizeX = m_Padding.left;
                sizeY = m_Padding.bottom;
                break;
            case StartCorner.BottomLeft:
                edgeY = RectTransform.Edge.Bottom;
                edgeX = RectTransform.Edge.Left;
                paddingX = m_Padding.left;
                paddingY = m_Padding.bottom;
                sizeX = m_Padding.right;
                sizeY = m_Padding.top;
                break;
            case StartCorner.BottomRight:
                edgeY = RectTransform.Edge.Bottom;
                edgeX = RectTransform.Edge.Right;
                paddingX = m_Padding.right;
                paddingY = m_Padding.bottom;
                sizeX = m_Padding.left;
                sizeY = m_Padding.top;
                break;
        }

        int axis = 0;
        int preferCount = m_FixedCount;
        float tem = 0;
        m_WrapSize = Vector2.zero;
        switch (m_Arrangement)
        {
            case Arrangement.HorizontalFlow:
                axis = 0;
                tem = paddingX;
                if (m_FixedCount <= 0)
                {
                    preferCount = Mathf.FloorToInt(rectTransform.rect.size.x / m_CellSize.x);
                    preferCount = preferCount > 0 ? preferCount : 1;
                }
                break;
            case Arrangement.VerticalFlow:
                axis = 1;
                tem = paddingY;
                if (m_FixedCount <= 0)
                {
                    preferCount = Mathf.FloorToInt(rectTransform.rect.size.y / m_CellSize.y);
                    preferCount = preferCount > 0 ? preferCount : 1;
                }
                break;
            case Arrangement.Horizontal:
                axis = 0;
                preferCount = int.MaxValue;
                tem = paddingX;
                break;
            case Arrangement.Vertical:
                axis = 1;
                preferCount = int.MaxValue;
                tem = paddingY;
                break;
        }

        int flag = 0;
        for (int i = 0; i < transform.childCount; i++)
        {
            if (flag >= preferCount)
            {
                if (axis == 0)
                {
                    tem = paddingX;
                    paddingY += m_Space.y + m_CellSize.y;
                }
                else
                {
                    tem = paddingY;
                    paddingX += m_Space.x + m_CellSize.x;
                }
                flag = 0;
            }
            var child = transform.GetChild(i) as RectTransform;
            if (child != null && child.gameObject.activeSelf)
            {
                if (axis == 0)
                {
                    child.SetInsetAndSizeFromParentEdge(edgeX, tem, m_CellSize.x);
                    child.SetInsetAndSizeFromParentEdge(edgeY, paddingY, m_CellSize.y);
                    tem += m_Space.x + m_CellSize.x;
                    if (tem - m_Space.x > m_WrapSize.x)
                        m_WrapSize.x = tem - m_Space.x;
                    m_WrapSize.y = paddingY + m_CellSize.y;
                }
                else
                {
                    child.SetInsetAndSizeFromParentEdge(edgeX, paddingX, m_CellSize.x);
                    child.SetInsetAndSizeFromParentEdge(edgeY, tem, m_CellSize.y);
                    tem += m_Space.y + m_CellSize.y;

                    if (tem - m_Space.y > m_WrapSize.y)
                        m_WrapSize.y = tem - m_Space.y;
                    m_WrapSize.x = paddingX + m_CellSize.x;
                }
                flag++;
            }
        }
        m_WrapSize.x += sizeX;
        m_WrapSize.y += sizeY;
    }

    public void SetPadding(int left, int right, int top, int bottom)
    {
        padding = new RectOffset(left, right, top, bottom);
    }

    public void SetStartCorner(int start)
    {
        startCorner = (StartCorner) start;
    }

    public void SetArrangement(int arrange)
    {
        arrangement = (Arrangement) arrange;
    }


    private void SetProperty<T>(ref T currentValue, T newValue)
    {
        if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
            return;
        currentValue = newValue;
        Rebuild();
    }

    [LuaInterface.NoToLua]
    public enum StartCorner
    {
        TopLeft = 0,
        TopRight = 1,
        BottomLeft = 2,
        BottomRight = 3
    }

    [LuaInterface.NoToLua]
    public enum Arrangement
    {
        //横向流动,第一排、第二排...
        HorizontalFlow = 0,
        //纵向流动,第一列、第二列...
        VerticalFlow = 1,
        //横排,相当于fixedCount=1效果
        Horizontal = 2,
        //纵排,相当于fixedCount=1效果
        Vertical = 3,
    }

}




#if UNITY_EDITOR

[UnityEditor.CustomEditor(typeof(UILayout))]
public class LayoutEditor : UnityEditor.Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        GUILayout.Space(10);
        if (GUILayout.Button("Rebuild"))
        {
            UILayout layout = target as UILayout;
            layout.Rebuild();
        }
    }
}
#endif

其中 SetInsetAndSizeFromParentEdge 是调用的系统方法,作用是 设置组件相对于父节点的位置Edge距离(参数1)且在该轴上的长度(参数2)

注:任何子节点的改变都不会像gridlayoutgroup一样刷新,需要手动调用Rebuild,因为觉得没有必要继承UIBehaviour而是继承了MonoBehaviour。 设置属性会自动刷新.

wrapSize 是rebuild后整个包裹区域应该的大小,可用于重新设置尺寸
posted @ 2018-11-20 22:54  Garsonlab  阅读(502)  评论(0编辑  收藏  举报