using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.UI; /// /// 文本控件,支持超链接 /// public class HyperlinkText : Text, IPointerClickHandler { /// /// 超链接信息类 /// private class HyperlinkInfo { public int startIndex; public int endIndex; public string name; public readonly List boxes = new List(); } /// /// 解析完最终的文本 /// private string m_OutputText; /// /// 超链接信息列表 /// private readonly List m_HrefInfos = new List(); /// /// 文本构造器 /// protected static readonly StringBuilder s_TextBuilder = new StringBuilder(); [Serializable] public class HrefClickEvent : UnityEvent { } [SerializeField] private HrefClickEvent m_OnHrefClick = new HrefClickEvent(); /// /// 超链接点击事件 /// public HrefClickEvent onHrefClick { get { return m_OnHrefClick; } set { m_OnHrefClick = value; } } /// /// 超链接正则 /// private static readonly Regex s_HrefRegex = new Regex(@"\n\s]+)>(.*?)()", RegexOptions.Singleline); private HyperlinkText mHyperlinkText; public System.Action onClickCallBack; public string GetHyperlinkInfo { get { return text; } } protected override void Awake() { base.Awake(); mHyperlinkText = GetComponent(); } protected override void OnEnable() { base.OnEnable(); mHyperlinkText.onHrefClick.AddListener(OnHyperlinkTextInfo); } protected override void OnDisable() { base.OnDisable(); mHyperlinkText.onHrefClick.RemoveListener(OnHyperlinkTextInfo); } public override void SetVerticesDirty() { base.SetVerticesDirty(); #if UNITY_EDITOR if (UnityEditor.PrefabUtility.GetPrefabType(this) == UnityEditor.PrefabType.Prefab) { return; } #endif // m_OutputText = GetOutputText(text); text = GetHyperlinkInfo; m_OutputText = GetOutputText(text); } protected override void OnPopulateMesh(VertexHelper toFill) { var orignText = m_Text; m_Text = m_OutputText; base.OnPopulateMesh(toFill); m_Text = orignText; UIVertex vert = new UIVertex(); // 处理超链接包围框 foreach (var hrefInfo in m_HrefInfos) { hrefInfo.boxes.Clear(); if (hrefInfo.startIndex >= toFill.currentVertCount) { continue; } // 将超链接里面的文本顶点索引坐标加入到包围框 toFill.PopulateUIVertex(ref vert, hrefInfo.startIndex); var pos = vert.position; var bounds = new Bounds(pos, Vector3.zero); for (int i = hrefInfo.startIndex, m = hrefInfo.endIndex; i < m; i++) { if (i >= toFill.currentVertCount) { break; } toFill.PopulateUIVertex(ref vert, i); pos = vert.position; if (pos.x < bounds.min.x) // 换行重新添加包围框 { hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size)); bounds = new Bounds(pos, Vector3.zero); } else { bounds.Encapsulate(pos); // 扩展包围框 } } hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size)); } } /// /// 获取超链接解析后的最后输出文本 /// /// protected virtual string GetOutputText(string outputText) { s_TextBuilder.Length = 0; m_HrefInfos.Clear(); var indexText = 0; foreach (Match match in s_HrefRegex.Matches(outputText)) { s_TextBuilder.Append(outputText.Substring(indexText, match.Index - indexText)); var group = match.Groups[1]; var hrefInfo = new HyperlinkInfo { startIndex = s_TextBuilder.Length * 4, // 超链接里的文本起始顶点索引 endIndex = (s_TextBuilder.Length + match.Groups[2].Length - 1) * 4 + 3, name = group.Value }; s_TextBuilder.Append(""); // 超链接颜色 m_HrefInfos.Add(hrefInfo); s_TextBuilder.Append(match.Groups[2].Value); s_TextBuilder.Append(""); indexText = match.Index + match.Length; } s_TextBuilder.Append(outputText.Substring(indexText, outputText.Length - indexText)); return s_TextBuilder.ToString(); } /// /// 点击事件检测是否点击到超链接文本 /// /// public void OnPointerClick(PointerEventData eventData) { Vector2 lp = Vector2.zero; RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out lp); foreach (var hrefInfo in m_HrefInfos) { var boxes = hrefInfo.boxes; for (var i = 0; i < boxes.Count; ++i) { if (boxes[i].Contains(lp)) { m_OnHrefClick.Invoke(hrefInfo.name); return; } } } } /// /// 当前点击超链接回调 /// /// 回调信息 private void OnHyperlinkTextInfo(string info) { onClickCallBack?.Invoke(info); // Debug.Log("超链接信息:" + info); } }