lcy
4 天以前 8e31a795cd6e271d085c2765dd85e8d72f2b27a5
Main/Component/UI/Core/TextLanguageAdapter.cs
@@ -3,9 +3,7 @@
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 文本组件类型枚举
/// </summary>
/// <summary>文本组件类型枚举</summary>
public enum TextComponentType
{
    None = 0,
@@ -14,13 +12,10 @@
    GradientText = 3
}
/// <summary>
/// 多语言排版配置项,存储单个语言的布局和适配数据
/// </summary>
/// <summary>单个语言的排版和适配数据节点</summary>
[Serializable]
public class LanguageConfigItem
{
    // ============ RectTransform 配置 ============
    [Header("RectTransform 配置")]
    public Vector2 anchoredPosition = Vector2.zero;
    public Vector2 sizeDelta = new Vector2(100f, 30f);
@@ -30,102 +25,83 @@
    public Vector3 localScale = Vector3.one;
    public Vector3 localRotation = Vector3.zero;
    // ============ Text 排版与适配配置 ============
    [Header("Text 排版与适配配置")]
    public Font font;
    public FontStyle fontStyle = FontStyle.Normal;
    public int fontSize = 14;
    public float lineSpacing = 1f;
    public HorizontalWrapMode horizontalOverflow = HorizontalWrapMode.Wrap;
    public VerticalWrapMode verticalOverflow = VerticalWrapMode.Truncate;
    public bool resizeTextForBestFit = false;
    public int resizeTextMinSize = 10;
    public int resizeTextMaxSize = 40;
    public TextAnchor alignment = TextAnchor.UpperLeft;
    public bool alignByGeometry = false;
    /// <summary> 将配置应用到 RectTransform </summary>
    public void ApplyToRectTransform(RectTransform rectTransform)
    public void ApplyToRectTransform(RectTransform rt)
    {
        if (rectTransform == null) return;
        rectTransform.anchorMin = anchorMin;
        rectTransform.anchorMax = anchorMax;
        rectTransform.pivot = pivot;
        rectTransform.anchoredPosition = anchoredPosition;
        rectTransform.sizeDelta = sizeDelta;
        rectTransform.localScale = localScale;
        rectTransform.localRotation = Quaternion.Euler(localRotation);
        if (rt == null) return;
        rt.anchorMin = anchorMin;
        rt.anchorMax = anchorMax;
        rt.pivot = pivot;
        rt.anchoredPosition = anchoredPosition;
        rt.sizeDelta = sizeDelta;
        rt.localScale = localScale;
        rt.localRotation = Quaternion.Euler(localRotation);
    }
    /// <summary> 从 RectTransform 读取配置 </summary>
    public void ReadFromRectTransform(RectTransform rectTransform)
    public void ReadFromRectTransform(RectTransform rt)
    {
        if (rectTransform == null) return;
        anchorMin = rectTransform.anchorMin;
        anchorMax = rectTransform.anchorMax;
        pivot = rectTransform.pivot;
        anchoredPosition = rectTransform.anchoredPosition;
        sizeDelta = rectTransform.sizeDelta;
        localScale = rectTransform.localScale;
        localRotation = rectTransform.localRotation.eulerAngles;
        if (rt == null) return;
        anchorMin = rt.anchorMin;
        anchorMax = rt.anchorMax;
        pivot = rt.pivot;
        anchoredPosition = rt.anchoredPosition;
        sizeDelta = rt.sizeDelta;
        localScale = rt.localScale;
        localRotation = rt.localRotation.eulerAngles;
    }
    /// <summary> 将配置应用到 Text </summary>
    public void ApplyToText(Text textComponent)
    public void ApplyToText(Text txt)
    {
        if (textComponent == null) return;
        if (font != null) textComponent.font = font;
        if (txt == null) return;
        if (font != null) txt.font = font;
        
        textComponent.fontStyle = fontStyle;
        textComponent.fontSize = fontSize;
        textComponent.lineSpacing = lineSpacing;
        textComponent.horizontalOverflow = horizontalOverflow;
        textComponent.verticalOverflow = verticalOverflow;
        textComponent.resizeTextForBestFit = resizeTextForBestFit;
        txt.fontStyle = fontStyle;
        txt.fontSize = fontSize;
        txt.lineSpacing = lineSpacing;
        txt.horizontalOverflow = horizontalOverflow;
        txt.verticalOverflow = verticalOverflow;
        txt.resizeTextForBestFit = resizeTextForBestFit;
        if (resizeTextForBestFit)
        {
            textComponent.resizeTextMinSize = resizeTextMinSize;
            textComponent.resizeTextMaxSize = resizeTextMaxSize;
            txt.resizeTextMinSize = resizeTextMinSize;
            txt.resizeTextMaxSize = resizeTextMaxSize;
        }
        textComponent.alignment = alignment;
        textComponent.alignByGeometry = alignByGeometry;
        txt.alignment = alignment;
        txt.alignByGeometry = alignByGeometry;
    }
    /// <summary> 从 Text 读取配置 </summary>
    public void ReadFromText(Text textComponent)
    public void ReadFromText(Text txt)
    {
        if (textComponent == null) return;
        font = textComponent.font;
        fontStyle = textComponent.fontStyle;
        fontSize = textComponent.fontSize;
        lineSpacing = textComponent.lineSpacing;
        horizontalOverflow = textComponent.horizontalOverflow;
        verticalOverflow = textComponent.verticalOverflow;
        resizeTextForBestFit = textComponent.resizeTextForBestFit;
        resizeTextMinSize = textComponent.resizeTextMinSize;
        resizeTextMaxSize = textComponent.resizeTextMaxSize;
        alignment = textComponent.alignment;
        alignByGeometry = textComponent.alignByGeometry;
        if (txt == null) return;
        font = txt.font;
        fontStyle = txt.fontStyle;
        fontSize = txt.fontSize;
        lineSpacing = txt.lineSpacing;
        horizontalOverflow = txt.horizontalOverflow;
        verticalOverflow = txt.verticalOverflow;
        resizeTextForBestFit = txt.resizeTextForBestFit;
        resizeTextMinSize = txt.resizeTextMinSize;
        resizeTextMaxSize = txt.resizeTextMaxSize;
        alignment = txt.alignment;
        alignByGeometry = txt.alignByGeometry;
    }
    /// <summary>
    /// 快速克隆配置
    /// 由于内部均为值类型或需浅拷贝的引用(Font),可以直接使用 MemberwiseClone
    /// </summary>
    public LanguageConfigItem Clone()
    {
        return (LanguageConfigItem)this.MemberwiseClone();
    }
    public LanguageConfigItem Clone() => (LanguageConfigItem)MemberwiseClone();
}
/// <summary>
/// 语言配置字典(Unity 内置序列化支持的双 List 结构)
/// </summary>
/// <summary>支持 Unity 序列化的字典</summary>
[Serializable]
public class LanguageConfigDictionary
{
@@ -136,16 +112,13 @@
    {
        if (string.IsNullOrEmpty(key)) return null;
        int index = keys.IndexOf(key);
        return (index >= 0 && index < values.Count) ? values[index] : null;
        return index >= 0 ? values[index] : null;
    }
    public void Set(string key, LanguageConfigItem value)
    {
        int index = keys.IndexOf(key);
        if (index >= 0 && index < values.Count)
        {
            values[index] = value;
        }
        if (index >= 0) values[index] = value;
        else
        {
            keys.Add(key);
@@ -161,48 +134,36 @@
        if (index >= 0)
        {
            keys.RemoveAt(index);
            if (index < values.Count) values.RemoveAt(index);
            values.RemoveAt(index);
        }
    }
}
/// <summary>
/// 文本多语言排版适配组件
/// </summary>
/// <summary>多语言排版适配器</summary>
[RequireComponent(typeof(RectTransform))]
public class TextLanguageAdapter : MonoBehaviour
{
    public const string DefaultLangId = "default"; // 提取常量避免硬编码错误
    public const string DefaultLangId = "default";
    [SerializeField]
    [Tooltip("每种语言的配置,使用语言ID作为key(如zh、en、ft),default为默认配置")]
    [SerializeField, Tooltip("语言配置字典")]
    private LanguageConfigDictionary m_LanguageConfigs = new LanguageConfigDictionary();
    [SerializeField]
    [Tooltip("自动检测到的文本组件类型")]
    [SerializeField, Tooltip("目标文本组件类型")]
    private TextComponentType m_TargetTextType = TextComponentType.None;
    [SerializeField]
    [Tooltip("关联的文本组件引用")]
    [SerializeField, Tooltip("关联的文本组件引用")]
    private Component m_TargetTextComponent;
    private bool m_IsApplied = false;
    #region 公共属性
    public TextComponentType TargetTextType => m_TargetTextType;
    public Component TargetTextComponent => m_TargetTextComponent; // 开放给 Editor 使用,避免反射
    public Component TargetTextComponent => m_TargetTextComponent;
    public LanguageConfigDictionary LanguageConfigs => m_LanguageConfigs;
    #endregion
    #region Unity生命周期
    protected void Awake()
    {
        DetectTargetComponent();
    }
    private void Awake() => DetectTargetComponent();
    protected void OnEnable()
    private void OnEnable()
    {
        // 保证仅应用一次排版以节省性能,除非在编辑器非运行模式下需要强刷
        if (!Application.isPlaying || !m_IsApplied)
        {
            string langId = Application.isPlaying ? Language.Id : DefaultLangId;
@@ -211,32 +172,14 @@
    }
#if UNITY_EDITOR
    protected void Reset()
    private void Reset()
    {
        DetectTargetComponent();
        if (!HasConfig(DefaultLangId))
        {
            ReadCurrentToConfig(DefaultLangId);
        }
        if (!HasConfig(DefaultLangId)) ReadCurrentToConfig(DefaultLangId);
    }
#endif
    #endregion
    #region 公共配置方法
    public LanguageConfigItem GetConfig(string languageId)
    {
        // 1. 尝试获取目标语言配置
        LanguageConfigItem config = m_LanguageConfigs.Get(languageId);
        if (config != null) return config;
        // 2. 找不到则回退到默认配置
        config = m_LanguageConfigs.Get(DefaultLangId);
        if (config != null) return config;
        Debug.LogError($"[TextLanguageAdapter] 找不到语言 {languageId} 的配置,且不存在 default 默认配置!");
        return null;
    }
    public LanguageConfigItem GetConfig(string languageId) => m_LanguageConfigs.Get(languageId);
    public void SetConfig(string languageId, LanguageConfigItem config) => m_LanguageConfigs.Set(languageId, config);
    public void RemoveConfig(string languageId) => m_LanguageConfigs.Remove(languageId);
    public bool HasConfig(string languageId) => m_LanguageConfigs.ContainsKey(languageId);
@@ -244,27 +187,20 @@
    public void ApplyConfig(string languageId)
    {
        LanguageConfigItem config = GetConfig(languageId);
        var config = GetConfig(languageId);
        if (config == null) return;
        if (TryGetComponent(out RectTransform rectTransform))
            config.ApplyToRectTransform(rectTransform);
        if (m_TargetTextComponent is Text textComponent)
            config.ApplyToText(textComponent);
        config.ApplyToRectTransform(GetComponent<RectTransform>());
        if (m_TargetTextComponent is Text txt) config.ApplyToText(txt);
        m_IsApplied = true;
    }
    public void ReadCurrentToConfig(string languageId)
    {
        LanguageConfigItem config = new LanguageConfigItem();
        if (TryGetComponent(out RectTransform rectTransform))
            config.ReadFromRectTransform(rectTransform);
        if (m_TargetTextComponent is Text textComponent)
            config.ReadFromText(textComponent);
        var config = new LanguageConfigItem();
        config.ReadFromRectTransform(GetComponent<RectTransform>());
        if (m_TargetTextComponent is Text txt) config.ReadFromText(txt);
        SetConfig(languageId, config);
    }
@@ -272,14 +208,10 @@
    public static string GetLanguageShowName(string languageId)
    {
        if (Language.languageShowDict != null && Language.languageShowDict.TryGetValue(languageId, out string showName))
        {
            return showName;
        }
        return languageId;
    }
    #endregion
    #region 私有组件检测
    private void DetectTargetComponent()
    {
        if (m_TargetTextComponent != null)
@@ -288,19 +220,11 @@
            return;
        }
        // 按优先级查找组件
        m_TargetTextComponent = FindComponent<GradientText>()
                             ?? FindComponent<TextEx>()
                             ?? (Component)FindComponent<Text>();
        m_TargetTextComponent = GetComponent<GradientText>() ?? GetComponentInChildren<GradientText>(true) as Component
                                ?? GetComponent<TextEx>() ?? GetComponentInChildren<TextEx>(true) as Component
                                ?? GetComponent<Text>() ?? GetComponentInChildren<Text>(true) as Component;
        DetermineTextType(m_TargetTextComponent);
    }
    /// <summary> 辅助方法:在自身和子物体中查找组件 </summary>
    private T FindComponent<T>() where T : Component
    {
        T comp = GetComponent<T>();
        return comp != null ? comp : GetComponentInChildren<T>(true);
    }
    private void DetermineTextType(Component component)
@@ -313,7 +237,6 @@
            _ => TextComponentType.None
        };
    }
    #endregion
#if UNITY_EDITOR
    [ContextMenu("刷新组件检测")]
@@ -331,9 +254,6 @@
    }
    [ContextMenu("应用默认配置")]
    public void Editor_ApplyDefaultConfig()
    {
        ApplyConfig(DefaultLangId);
    }
    public void Editor_ApplyDefaultConfig() => ApplyConfig(DefaultLangId);
#endif
}