yyl
2026-05-11 51b0f6ed9f4e1d3bb6f8144470b46908c7699a96
Main/System/Message/RichText.cs
@@ -7,6 +7,7 @@
using System;
using System.Linq;
using System.Text;
using Cysharp.Threading.Tasks;
public class RichText : Text, IPointerClickHandler
{
    /// <summary>
@@ -113,7 +114,7 @@
        set
        {
            m_AutoNewLine = value;
            SetRichTextDirty();
            SetRichTextDirty().Forget();
        }
    }
@@ -181,8 +182,39 @@
        unline = transform.GetComponentInChildren<TextUnline>();
        if (unline == null)
        {
            GameObject obj = Resources.Load("UIComp/TextUnline") as GameObject;
            // GameObject obj = UILoader.LoadPrefab("TextUnline") as GameObject;
            // GameObject obj = await BuiltInLoader.LoadPrefabAsync("TextUnline");
            // if (this == null) return;
            // obj = Instantiate(obj);
            // obj.transform.SetParent(transform);
            // obj.transform.localScale = Vector3.one;
            // unline = obj.GetComponent<TextUnline>();
            // unline.raycastTarget = false;
            BuiltInLoader.LoadPrefabAsync("TextUnline").ContinueWith(prefab =>
            {
                if (this == null) return;
                GameObject obj = Instantiate(prefab);
                obj.transform.SetParent(transform);
                obj.transform.localScale = Vector3.one;
                unline = obj.GetComponent<TextUnline>();
                unline.raycastTarget = false;
            }).Forget();
        }
    }
    public async UniTask AwakeAsync()
    {
#if UNITY_EDITOR
        if (UnityEditor.PrefabUtility.GetPrefabType(this) == UnityEditor.PrefabType.Prefab)
        {
            return;
        }
#endif
        unline = transform.GetComponentInChildren<TextUnline>();
        if (unline == null)
        {
            GameObject obj = await BuiltInLoader.LoadPrefabAsync("TextUnline");
            if (this == null) return;
            obj = Instantiate(obj);
            obj.transform.SetParent(transform);
            obj.transform.localScale = Vector3.one;
@@ -263,12 +295,14 @@
    #endregion
    #region 解析
    private string GetOutputText(string _text)
    private async UniTask<string> GetOutputText(string _text)
    {
        string result = _text;
        result = GetExtenalData(result);
        result = GetTaskInfo(result);
        result = RichTextMgr.Inst.Analysis(result, out m_ImgList, out m_HrefList, this);
        result = await RichTextMgr.Inst.Analysis(result, this);
        m_ImgList = RichTextMgr.Inst.GetImgList();
        m_HrefList = RichTextMgr.Inst.GetHrefList();
        return result;
    }
    #endregion
@@ -504,8 +538,26 @@
            {
                font = FontUtility.preferred;
            }
            // 添加 null 检查
            if (font == null || rectTransform == null || cachedTextGeneratorForLayout == null || string.IsNullOrEmpty(m_OutputText))
            {
                return 0f;
            }
            var settings = GetGenerationSettings(Vector2.zero);
            return cachedTextGeneratorForLayout.GetPreferredWidth(m_OutputText, settings) / pixelsPerUnit;
            float width = 0f;
            try
            {
                width = cachedTextGeneratorForLayout.GetPreferredWidth(m_OutputText, settings) / pixelsPerUnit;
            }
            catch (Exception ex)
            {
                Debug.LogError($"GetPreferredWidth failed: {ex.Message}");
                width = 0f;
            }
            return width;
        }
    }
@@ -517,9 +569,26 @@
            {
                font = FontUtility.preferred;
            }
            // 添加 null 检查
            if (font == null || rectTransform == null || cachedTextGeneratorForLayout == null || string.IsNullOrEmpty(m_OutputText))
            {
                return 0f;
            }
            var settings = GetGenerationSettings(new Vector2(rectTransform.rect.size.x, 0.0f));
            float _height = cachedTextGeneratorForLayout.GetPreferredHeight(m_OutputText, settings) / pixelsPerUnit;
            return _height;
            float height = 0f;
            try
            {
                height = cachedTextGeneratorForLayout.GetPreferredHeight(m_OutputText, settings) / pixelsPerUnit;
            }
            catch (Exception ex)
            {
                Debug.LogError($"GetPreferredHeight failed: {ex.Message}");
                height = 0f;
            }
            return height;
        }
    }
    #endregion
@@ -583,7 +652,7 @@
    public void SetReplaceInfo(Dictionary<string, string> _infoDic)
    {
        extenalDataDic = _infoDic;
        SetRichTextDirty();
        SetRichTextDirty().Forget();
    }
    private string GetTaskInfo(string val)
@@ -619,6 +688,13 @@
    #region 执行事件
    public void OnPointerClick(PointerEventData eventData)
    {
        // 检查组件是否已禁用或销毁,避免空指针解引用
        if (!this.isActiveAndEnabled)
        {
            return;
        }
        OnClick?.Invoke();
        if (HrefClick)
        {
            Vector2 lp;
@@ -640,7 +716,6 @@
                }
            }
        }
        if (OnClick != null) OnClick();
    }
    public void OnImgClick()
@@ -665,6 +740,16 @@
    private static StringBuilder textBuilder = new StringBuilder();
    private Dictionary<int, Match> matchDics = new Dictionary<int, Match>();
    // 字符宽度缓存,避免重复计算相同字符
    private Dictionary<string, float> charWidthCache = new Dictionary<string, float>();
    protected override void OnDisable()
    {
        base.OnDisable();
        // 组件禁用时清空缓存,防止内存泄漏
        charWidthCache.Clear();
    }
    private bool IsModifySize(int _index,out int _size)
    {
@@ -724,6 +809,14 @@
        {
            font = FontUtility.preferred;
        }
        // 添加 null 检查
        if (font == null || rectTransform == null || cachedTextGeneratorForLayout == null)
        {
            Debug.LogWarning("SetFitterSize: font, rectTransform or cachedTextGeneratorForLayout is null");
            return;
        }
        var settings = GetGenerationSettings(Vector2.zero);
        float cache = 0;
@@ -732,6 +825,7 @@
        float ratio = GetResolutionRatio();
        matchDics.Clear();
        charWidthCache.Clear();
        foreach (Match match in ImgAnalysis.Unity_Img_Regex.Matches(fitterText))
        {
@@ -802,7 +896,8 @@
                }
                else
                {
                    cache = cachedTextGeneratorForLayout.GetPreferredWidth(match.Value, settings) * ratio;
                    cache = GetCharWidthCached(match.Value, settings, ratio);
                    if (width + cache > (rectTransform.rect.width - 5))
                    {
                        CacluHrefAndImgIndex(Mathf.Max(0, i - 1));
@@ -820,19 +915,16 @@
            else
            {
                var _size = 0;
                var _cacheFontSize = fontSize;
                // 不再修改 fontSize,使用临时 TextGenerator 计算不同字号的宽度
                if (_modifySize && IsModifySize(i, out _size))
                {
                    fontSize = _size;
                    settings = GetGenerationSettings(Vector2.zero);
                    cache = cachedTextGeneratorForLayout.GetPreferredWidth(fitterText[i].ToString(), settings) * ratio;
                    fontSize = _cacheFontSize;
                    settings = GetGenerationSettings(Vector2.zero);
                    cache = GetCharWidthWithCustomSize(fitterText[i], _size, ratio);
                }
                else
                {
                    cache = cachedTextGeneratorForLayout.GetPreferredWidth(fitterText[i].ToString(), settings) * ratio;
                    cache = GetCharWidthCached(fitterText[i].ToString(), settings, ratio);
                }
                if (width + cache > (rectTransform.rect.width - 5))
                {
                    CacluHrefAndImgIndex(Mathf.Max(0, i - 1));
@@ -848,6 +940,65 @@
        }
        CacluHrefAndImgIndex();
        m_OutputText = textBuilder.ToString();
    }
    /// <summary>
    /// 获取字符宽度(带缓存)
    /// </summary>
    private float GetCharWidthCached(string charStr, TextGenerationSettings settings, float ratio)
    {
        string cacheKey = $"{fontSize}_{charStr}";
        if (charWidthCache.ContainsKey(cacheKey))
        {
            return charWidthCache[cacheKey];
        }
        float width = 0;
        try
        {
            width = cachedTextGeneratorForLayout.GetPreferredWidth(charStr, settings) * ratio;
        }
        catch (Exception ex)
        {
            Debug.LogError($"GetPreferredWidth failed for '{charStr}': {ex.Message}");
            width = 0;
        }
        charWidthCache[cacheKey] = width;
        return width;
    }
    /// <summary>
    /// 使用自定义字号计算字符宽度(不修改原始 Text 组件的 fontSize)
    /// </summary>
    private float GetCharWidthWithCustomSize(char c, int customFontSize, float ratio)
    {
        string charStr = c.ToString();
        string cacheKey = $"{customFontSize}_{charStr}";
        if (charWidthCache.ContainsKey(cacheKey))
        {
            return charWidthCache[cacheKey];
        }
        float width = 0;
        try
        {
            // 创建临时的 TextGenerationSettings,使用自定义字号
            TextGenerationSettings tempSettings = GetGenerationSettings(Vector2.zero);
            tempSettings.fontSize = customFontSize;
            width = cachedTextGeneratorForLayout.GetPreferredWidth(charStr, tempSettings) * ratio;
        }
        catch (Exception ex)
        {
            Debug.LogError($"GetPreferredWidth failed for '{charStr}' with fontSize {customFontSize}: {ex.Message}");
            width = 0;
        }
        charWidthCache[cacheKey] = width;
        return width;
    }
    private void CacluHrefAndImgIndex(int index)
@@ -907,7 +1058,7 @@
                value = string.Empty;
            }
            m_RichText = value;
            SetRichTextDirty();
            SetRichTextDirty().Forget();
        }
    }
@@ -918,10 +1069,10 @@
    }
#endif
    private void SetRichTextDirty()
    private async UniTask SetRichTextDirty()
    {
        m_RichText = UIHelper.ReplaceNewLine(m_RichText);
        m_OutputText = GetOutputText(m_RichText);
        m_OutputText = await GetOutputText(m_RichText);
        if (AutoNewLine)
        {
            SetFitterSize();