1. 程式人生 > >【UGUI】自動佈局

【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後整個包裹區域應該的大小,可用於重新設定尺寸