Unity雜文——UI父節(jié)點隨子節(jié)點自適應

原文地址

簡介

在UI的開發(fā)過程中惭蹂,經(jīng)常會遇到Image隨子節(jié)點的文字變化自動縮放惦积,就是拿Image當背景。筆者遇到這種問題每次都是利用Layout+Content Size Fitter來完成的学密,筆者想了想每次都要加兩個組件,并且Layout只用到了隨自己點自適應的功能伺帘,于是筆者便想辦法把兩個功能合成一個腳本來實現(xiàn)需求昭躺,于是便有了下面的腳本。

代碼

代碼如下:

using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

/// <summary>
/// 未完成伪嫁,暫時別用
/// </summary>
[AddComponentMenu("Layout/Rect Transform Fitter", 142)]
[ExecuteAlways]
[RequireComponent(typeof(RectTransform))]
public class RectTransformFit : UIBehaviour, ILayoutGroup
{
    [SerializeField] protected RectTransform m_RectChildren;
    [SerializeField] protected RectOffset m_Padding = new RectOffset();
    [SerializeField] protected TextAnchor m_ChildAlignment = TextAnchor.UpperLeft;
    [SerializeField] protected bool m_ChildControlWidth = false;
    [SerializeField] protected bool m_ChildControlHeight = false;
    [SerializeField] protected ContentSizeFitter.FitMode m_HorizontalFit = ContentSizeFitter.FitMode.Unconstrained;
    [SerializeField] protected ContentSizeFitter.FitMode m_VerticalFit = ContentSizeFitter.FitMode.Unconstrained;

    public RectOffset padding
    {
        get => m_Padding;
        set => SetProperty(ref m_Padding, value);
    }
    public TextAnchor childAlignment { get => m_ChildAlignment;
        set => SetProperty(ref m_ChildAlignment, value);
    }
    public bool childControlWidth
    {
        get => m_ChildControlWidth;
        set => SetProperty(ref m_ChildControlWidth, value);
    }
    public bool childControlHeight
    {
        get => m_ChildControlHeight;
        set => SetProperty(ref m_ChildControlHeight, value);
    }
    public ContentSizeFitter.FitMode horizontalFit
    {
        get => m_HorizontalFit;
        set
        {
            if (SetPropertyUtility.SetStruct(ref m_HorizontalFit, value)) SetDirty();
        }
    }
    public ContentSizeFitter.FitMode verticalFit
    {
        get => m_VerticalFit;
        set
        {
            if (SetPropertyUtility.SetStruct(ref m_VerticalFit, value)) SetDirty();
        }
    }

    [NonSerialized] private RectTransform m_Rect;
    protected RectTransform rectTransform
    {
        get
        {
            if (m_Rect == null)
                m_Rect = GetComponent<RectTransform>();
            return m_Rect;
        }
    }

#pragma warning disable 649
    private DrivenRectTransformTracker m_Tracker;
#pragma warning restore 649

    private void OnEnable()
    {
        m_Rect ??= GetComponent<RectTransform>();
        SetDirty();
    }

    protected override void OnRectTransformDimensionsChange()
    {
        SetDirty();
    }

    protected override void OnDisable()
    {
        m_Tracker.Clear();
        base.OnDisable();
    }

    /// <summary>
    /// Calculate and apply the horizontal component of the size to the RectTransform
    /// </summary>
    public void SetLayoutHorizontal()
    {
        m_Tracker.Clear();
        if (m_RectChildren == null || !m_ChildControlWidth)
        {
            SetDirty();
            return;
        }
        HandleSelfFittingAlongAxis(0, m_RectChildren);
    }

    /// <summary>
    /// Calculate and apply the vertical component of the size to the RectTransform
    /// </summary>
    public void SetLayoutVertical()
    {
        if (m_RectChildren == null || !m_ChildControlHeight)
        {
            SetDirty();
            return;
        }
        HandleSelfFittingAlongAxis(1, m_RectChildren);
    }

    private void HandleSelfFittingAlongAxis(int axis, RectTransform rectChild)
    {
        if (rectChild == null) return;
        var fitting = (axis == 0 ? horizontalFit : verticalFit);
        if (fitting == ContentSizeFitter.FitMode.Unconstrained)
        {
            // Keep a reference to the tracked transform, but don't control its properties:
            m_Tracker.Add(this, rectChild, DrivenTransformProperties.None);
            return;
        }

        m_Tracker.Add(this, rectChild,
            (axis == 0 ? DrivenTransformProperties.SizeDeltaX : DrivenTransformProperties.SizeDeltaY));

        // Set size to min or preferred size
        rectChild.SetSizeWithCurrentAnchors((RectTransform.Axis)axis,
            fitting == ContentSizeFitter.FitMode.MinSize
                ? LayoutUtility.GetMinSize(rectChild, axis)
                : LayoutUtility.GetPreferredSize(rectChild, axis));

        SetDirty();
    }

    /// <summary>
    /// Helper method used to set a given property if it has changed.
    /// </summary>
    /// <param name="currentValue">A reference to the member value.</param>
    /// <param name="newValue">The new value.</param>
    protected void SetProperty<T>(ref T currentValue, T newValue)
    {
        if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
            return;
        currentValue = newValue;
        SetDirty();
    }

    protected void SetDirty()
    {
        if (!IsActive())
            return;

        RefreshRect();
        LayoutRebuilder.MarkLayoutForRebuild(m_RectChildren);
        LayoutRebuilder.MarkLayoutForRebuild(m_Rect);
    }

    public void RefreshRect()
    {
        if (m_RectChildren == null) return;
        Vector2 anchoredPos;
        var childSize = m_RectChildren.sizeDelta;
        var width = childSize.x + padding.left + padding.right;
        var height = childSize.y + padding.top + padding.bottom;
        var rectSize = rectTransform.sizeDelta;
        if (horizontalFit != ContentSizeFitter.FitMode.Unconstrained &&
            verticalFit != ContentSizeFitter.FitMode.Unconstrained)
        {
            rectTransform.sizeDelta = new Vector2(width, height);
        }
        else if (horizontalFit != ContentSizeFitter.FitMode.Unconstrained)
        {
            rectTransform.sizeDelta = new Vector2(width, rectSize.y);
        }
        else if (verticalFit != ContentSizeFitter.FitMode.Unconstrained)
        {
            rectTransform.sizeDelta = new Vector2(rectSize.x, height);
        }
        rectSize = rectTransform.sizeDelta;
        var oldPos = rectTransform.anchoredPosition;
        var oldPivot = rectTransform.pivot;

        switch (m_ChildAlignment)
        {
            case TextAnchor.UpperLeft:
                rectTransform.pivot = new Vector2(0, 1);
                anchoredPos = new Vector2(padding.left, -padding.top);
                break;
            case TextAnchor.UpperCenter:
                rectTransform.pivot = new Vector2(0.5f, 1);
                anchoredPos = new Vector2(0, -padding.top);
                break;
            case TextAnchor.UpperRight:
                rectTransform.pivot = new Vector2(1, 1);
                anchoredPos = new Vector2(-padding.right, -padding.top);
                break;
            case TextAnchor.MiddleLeft:
                rectTransform.pivot = new Vector2(0, 0.5f);
                anchoredPos = new Vector2(padding.left, 0);
                break;
            case TextAnchor.MiddleCenter:
                rectTransform.pivot = new Vector2(0.5f, 0);
                anchoredPos = new Vector2(0, 0);
                break;
            case TextAnchor.MiddleRight:
                rectTransform.pivot = new Vector2(1, 0);
                anchoredPos = new Vector2(-padding.right, 0);
                break;
            case TextAnchor.LowerLeft:
                rectTransform.pivot = new Vector2(0, 0);
                anchoredPos = new Vector2(padding.left, padding.bottom);
                break;
            case TextAnchor.LowerCenter:
                rectTransform.pivot = new Vector2(0.5f, 0);
                anchoredPos = new Vector2(0, padding.bottom);
                break;
            case TextAnchor.LowerRight:
                rectTransform.pivot = new Vector2(1, 0);
                anchoredPos = new Vector2(-padding.right, padding.bottom);
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
        var pivot = rectTransform.pivot;
        rectTransform.anchoredPosition = new Vector2(oldPos.x + rectSize.x * (pivot.x - oldPivot.x),
            oldPos.y + rectSize.y * (pivot.y - oldPivot.y));
        m_RectChildren.anchorMax = pivot;
        m_RectChildren.anchorMin = pivot;
        m_RectChildren.pivot = pivot;
        m_RectChildren.anchoredPosition = anchoredPos;
    }

#if UNITY_EDITOR

    protected override void OnValidate()
    {
        SetDirty();
    }

#endif
}

需要支持的腳本(源碼抄來的SetPropertyUtility)

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

internal static class SetPropertyUtility
{
    private const float Tolerance = 0.000001f;                 //通過此值判斷值是否發(fā)生變化

    public static bool SetColor(ref Color currentValue, Color newValue)
    {
        if (Math.Abs(currentValue.r - newValue.r) < Tolerance &&
            Math.Abs(currentValue.g - newValue.g) < Tolerance &&
            Math.Abs(currentValue.b - newValue.b) < Tolerance &&
            Math.Abs(currentValue.a - newValue.a) < Tolerance)
            return false;

        currentValue = newValue;
        return true;
    }

    public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct
    {
        if (EqualityComparer<T>.Default.Equals(currentValue, newValue))
            return false;

        currentValue = newValue;
        return true;
    }

    public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
    {
        if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
            return false;

        currentValue = newValue;
        return true;
    }
}

編輯器顯示Editor代碼

代碼如下:

using System;
using UnityEditor;
using UnityEditor.UI;
using UnityEngine;

[CustomEditor(typeof(RectTransformFit))]
public class RectTransformFitEditor : SelfControllerEditor
{

    SerializedProperty m_Padding;
    SerializedProperty m_ChildAlignment;
    SerializedProperty m_RectChildren;
    SerializedProperty m_HorizontalFit;
    SerializedProperty m_VerticalFit;
    SerializedProperty m_ChildControlWidth;
    SerializedProperty m_ChildControlHeight;

    protected void OnEnable()
    {
        m_Padding = serializedObject.FindProperty("m_Padding");
        m_ChildAlignment = serializedObject.FindProperty("m_ChildAlignment");
        m_RectChildren = serializedObject.FindProperty("m_RectChildren");
        m_ChildControlWidth = serializedObject.FindProperty("m_ChildControlWidth");
        m_ChildControlHeight = serializedObject.FindProperty("m_ChildControlHeight");
        m_HorizontalFit = serializedObject.FindProperty("m_HorizontalFit");
        m_VerticalFit = serializedObject.FindProperty("m_VerticalFit");
    }


    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        EditorGUILayout.PropertyField(m_Padding, true);
        EditorGUILayout.PropertyField(m_ChildAlignment, true);
        EditorGUILayout.PropertyField(m_RectChildren, true);

        Rect rect = EditorGUILayout.GetControlRect();
        rect = EditorGUI.PrefixLabel(rect, -1, EditorGUIUtility.TrTextContent("Control Child Size"));
        rect.width = Mathf.Max(50, (rect.width - 4) / 3);
        EditorGUIUtility.labelWidth = 50;
        ToggleLeft(rect, m_ChildControlWidth, EditorGUIUtility.TrTextContent("Width"));
        rect.x += rect.width + 2;
        ToggleLeft(rect, m_ChildControlHeight, EditorGUIUtility.TrTextContent("Height"));
        EditorGUIUtility.labelWidth = 0;

        EditorGUILayout.PropertyField(m_HorizontalFit, true);
        EditorGUILayout.PropertyField(m_VerticalFit, true);
        serializedObject.ApplyModifiedProperties();
    }

    void ToggleLeft(Rect position, SerializedProperty property, GUIContent label)
    {
        bool toggle = property.boolValue;
        EditorGUI.showMixedValue = property.hasMultipleDifferentValues;
        EditorGUI.BeginChangeCheck();
        int oldIndent = EditorGUI.indentLevel;
        EditorGUI.indentLevel = 0;
        toggle = EditorGUI.ToggleLeft(position, label, toggle);
        EditorGUI.indentLevel = oldIndent;
        if (EditorGUI.EndChangeCheck())
        {
            property.boolValue = property.hasMultipleDifferentValues || !property.boolValue;
        }
        EditorGUI.showMixedValue = false;
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末领炫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子张咳,更是在濱河造成了極大的恐慌帝洪,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脚猾,死亡現(xiàn)場離奇詭異葱峡,居然都是意外死亡,警方通過查閱死者的電腦和手機龙助,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門砰奕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人提鸟,你說我怎么就攤上這事军援。” “怎么了称勋?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵胸哥,是天一觀的道長。 經(jīng)常有香客問我赡鲜,道長空厌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任银酬,我火速辦了婚禮嘲更,結果婚禮上,老公的妹妹穿的比我還像新娘揩瞪。我一直安慰自己哮内,他們只是感情好,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布壮韭。 她就那樣靜靜地躺著北发,像睡著了一般。 火紅的嫁衣襯著肌膚如雪喷屋。 梳的紋絲不亂的頭發(fā)上琳拨,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天,我揣著相機與錄音屯曹,去河邊找鬼狱庇。 笑死惊畏,一個胖子當著我的面吹牛,可吹牛的內容都是我干的密任。 我是一名探鬼主播颜启,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼浪讳!你這毒婦竟也來了缰盏?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤淹遵,失蹤者是張志新(化名)和其女友劉穎口猜,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體透揣,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡济炎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辐真。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片须尚。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖侍咱,靈堂內的尸體忽然破棺而出耐床,到底是詐尸還是另有隱情,我是刑警寧澤放坏,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布咙咽,位于F島的核電站老玛,受9級特大地震影響淤年,放射性物質發(fā)生泄漏。R本人自食惡果不足惜蜡豹,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一麸粮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镜廉,春花似錦弄诲、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至塔插,卻和暖如春梗摇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背想许。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工伶授, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留断序,地道東北人。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓糜烹,卻偏偏與公主長得像违诗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子疮蹦,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內容