HyperlinkText.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Text.RegularExpressions;
  5. using UnityEngine;
  6. using UnityEngine.Events;
  7. using UnityEngine.EventSystems;
  8. using UnityEngine.UI;
  9. /// <summary>
  10. /// 文本控件,支持超链接
  11. /// </summary>
  12. public class HyperlinkText : Text, IPointerClickHandler
  13. {
  14. /// <summary>
  15. /// 超链接信息类
  16. /// </summary>
  17. private class HyperlinkInfo
  18. {
  19. public int startIndex;
  20. public int endIndex;
  21. public string name;
  22. public readonly List<Rect> boxes = new List<Rect>();
  23. }
  24. /// <summary>
  25. /// 解析完最终的文本
  26. /// </summary>
  27. private string m_OutputText;
  28. /// <summary>
  29. /// 超链接信息列表
  30. /// </summary>
  31. private readonly List<HyperlinkInfo> m_HrefInfos = new List<HyperlinkInfo>();
  32. /// <summary>
  33. /// 文本构造器
  34. /// </summary>
  35. protected static readonly StringBuilder s_TextBuilder = new StringBuilder();
  36. [Serializable]
  37. public class HrefClickEvent : UnityEvent<string>
  38. {
  39. }
  40. [SerializeField] private HrefClickEvent m_OnHrefClick = new HrefClickEvent();
  41. /// <summary>
  42. /// 超链接点击事件
  43. /// </summary>
  44. public HrefClickEvent onHrefClick
  45. {
  46. get { return m_OnHrefClick; }
  47. set { m_OnHrefClick = value; }
  48. }
  49. /// <summary>
  50. /// 超链接正则
  51. /// </summary>
  52. private static readonly Regex s_HrefRegex = new Regex(@"<a href=([^>\n\s]+)>(.*?)(</a>)", RegexOptions.Singleline);
  53. private HyperlinkText mHyperlinkText;
  54. public System.Action<string> onClickCallBack;
  55. public string GetHyperlinkInfo
  56. {
  57. get { return text; }
  58. }
  59. protected override void Awake()
  60. {
  61. base.Awake();
  62. mHyperlinkText = GetComponent<HyperlinkText>();
  63. }
  64. protected override void OnEnable()
  65. {
  66. base.OnEnable();
  67. mHyperlinkText.onHrefClick.AddListener(OnHyperlinkTextInfo);
  68. }
  69. protected override void OnDisable()
  70. {
  71. base.OnDisable();
  72. mHyperlinkText.onHrefClick.RemoveListener(OnHyperlinkTextInfo);
  73. }
  74. public override void SetVerticesDirty()
  75. {
  76. base.SetVerticesDirty();
  77. #if UNITY_EDITOR
  78. if (UnityEditor.PrefabUtility.GetPrefabType(this) == UnityEditor.PrefabType.Prefab)
  79. {
  80. return;
  81. }
  82. #endif
  83. // m_OutputText = GetOutputText(text);
  84. text = GetHyperlinkInfo;
  85. m_OutputText = GetOutputText(text);
  86. }
  87. protected override void OnPopulateMesh(VertexHelper toFill)
  88. {
  89. var orignText = m_Text;
  90. m_Text = m_OutputText;
  91. base.OnPopulateMesh(toFill);
  92. m_Text = orignText;
  93. UIVertex vert = new UIVertex();
  94. // 处理超链接包围框
  95. foreach (var hrefInfo in m_HrefInfos)
  96. {
  97. hrefInfo.boxes.Clear();
  98. if (hrefInfo.startIndex >= toFill.currentVertCount)
  99. {
  100. continue;
  101. }
  102. // 将超链接里面的文本顶点索引坐标加入到包围框
  103. toFill.PopulateUIVertex(ref vert, hrefInfo.startIndex);
  104. var pos = vert.position;
  105. var bounds = new Bounds(pos, Vector3.zero);
  106. for (int i = hrefInfo.startIndex, m = hrefInfo.endIndex; i < m; i++)
  107. {
  108. if (i >= toFill.currentVertCount)
  109. {
  110. break;
  111. }
  112. toFill.PopulateUIVertex(ref vert, i);
  113. pos = vert.position;
  114. if (pos.x < bounds.min.x) // 换行重新添加包围框
  115. {
  116. hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
  117. bounds = new Bounds(pos, Vector3.zero);
  118. }
  119. else
  120. {
  121. bounds.Encapsulate(pos); // 扩展包围框
  122. }
  123. }
  124. hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
  125. }
  126. }
  127. /// <summary>
  128. /// 获取超链接解析后的最后输出文本
  129. /// </summary>
  130. /// <returns></returns>
  131. protected virtual string GetOutputText(string outputText)
  132. {
  133. s_TextBuilder.Length = 0;
  134. m_HrefInfos.Clear();
  135. var indexText = 0;
  136. foreach (Match match in s_HrefRegex.Matches(outputText))
  137. {
  138. s_TextBuilder.Append(outputText.Substring(indexText, match.Index - indexText));
  139. var group = match.Groups[1];
  140. var hrefInfo = new HyperlinkInfo
  141. {
  142. startIndex = s_TextBuilder.Length * 4, // 超链接里的文本起始顶点索引
  143. endIndex = (s_TextBuilder.Length + match.Groups[2].Length - 1) * 4 + 3,
  144. name = group.Value
  145. };
  146. s_TextBuilder.Append("<b><color=#CC5B13>"); // 超链接颜色
  147. m_HrefInfos.Add(hrefInfo);
  148. s_TextBuilder.Append(match.Groups[2].Value);
  149. s_TextBuilder.Append("</color></b>");
  150. indexText = match.Index + match.Length;
  151. }
  152. s_TextBuilder.Append(outputText.Substring(indexText, outputText.Length - indexText));
  153. return s_TextBuilder.ToString();
  154. }
  155. /// <summary>
  156. /// 点击事件检测是否点击到超链接文本
  157. /// </summary>
  158. /// <param name="eventData"></param>
  159. public void OnPointerClick(PointerEventData eventData)
  160. {
  161. Vector2 lp = Vector2.zero;
  162. RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position,
  163. eventData.pressEventCamera, out lp);
  164. foreach (var hrefInfo in m_HrefInfos)
  165. {
  166. var boxes = hrefInfo.boxes;
  167. for (var i = 0; i < boxes.Count; ++i)
  168. {
  169. if (boxes[i].Contains(lp))
  170. {
  171. m_OnHrefClick.Invoke(hrefInfo.name);
  172. return;
  173. }
  174. }
  175. }
  176. }
  177. /// <summary>
  178. /// 当前点击超链接回调
  179. /// </summary>
  180. /// <param name="info">回调信息</param>
  181. private void OnHyperlinkTextInfo(string info)
  182. {
  183. onClickCallBack?.Invoke(info);
  184. // Debug.Log("超链接信息:" + info);
  185. }
  186. }