| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Text;
- using System.Text.RegularExpressions;
- using UnityEngine;
- using UnityEngine.Events;
- using UnityEngine.EventSystems;
- using UnityEngine.UI;
- /// <summary>
- /// TextHyperlink 支持Unity超链接富文本语法
- /// 例如<a href=https://aws.amazon.com/privacy/>https://aws.amazon.com/privacy/</a>
- /// 带颜色的超链接:
- /// <a href=https://aws.amazon.com/privacy/><color=#FF0000>https://aws.amazon.com/privacy/</color></a>
- /// </summary>
- public class HyperlinkText : Text, IPointerClickHandler
- {
- /// <summary>
- /// 超链接信息类
- /// </summary>
- private class HyperlinkInfo
- {
- //起始Index
- public int StartIndex;
- //结束Index
- public int EndIndex;
- //内容
- public string Name;
- //包围框
- public List<Rect> BoxList = new List<Rect>();
- }
- #region 私有变量
- //超链接正则
- private static Regex _hrefRegex = new Regex(@"<a href=([^>\n\s]+)>(.*?)(</a>)", RegexOptions.Singleline);
- //颜色正则
- private static Regex _colorRegex = new Regex(@"<color=([^>\n\s]+)>(.*?)(</color>)", RegexOptions.Singleline);
- //超链接信息列表
- private List<HyperlinkInfo> _hyperlinkInfoList = new List<HyperlinkInfo>();
- //显示包围盒范围,想用的话输入true
- private bool _showTestBound = true;
- #endregion
- #region 公有变量
- #endregion
- #region 生命周期
- protected override void OnPopulateMesh(VertexHelper toFill)
- {
- base.OnPopulateMesh(toFill);
- InitHyperlinkInfo();
- InitHyperlinkBox(toFill);
- }
- protected override void Start()
- {
- if (_showTestBound)
- {
- AddVisibleBound();
- }
- }
- #endregion
- #region 公有方法
- #endregion
- #region 动作
- public Action<string> onClickHref;
- public void OnPointerClick(PointerEventData eventData)
- {
- Vector2 localPoint;
- RectTransformUtility.ScreenPointToLocalPointInRectangle(
- rectTransform, eventData.position, eventData.pressEventCamera, out localPoint);
- foreach (HyperlinkInfo hyperlinkInfo in _hyperlinkInfoList)
- {
- var boxeList = hyperlinkInfo.BoxList;
- for (var i = 0; i < boxeList.Count; ++i)
- {
- if (boxeList[i].Contains(localPoint))
- {
- //打开网址
- //this.Log("TextHyperlink", hyperlinkInfo.Name);
- // Application.OpenURL(hyperlinkInfo.Name);
- onClickHref?.Invoke(hyperlinkInfo.Name);
- return;
- }
- }
- }
- }
- #endregion
- #region 私有方法
- /// <summary>
- /// 初始化连接信息
- /// </summary>
- private void InitHyperlinkInfo()
- {
- //这个值不用,就是个存根。要的是执行后面那个方法
- string outputText = GetOutputText(text);
- }
- /// <summary>
- /// 初始化连接包围框
- /// </summary>
- /// <param name="toFill"></param>
- private void InitHyperlinkBox(VertexHelper toFill)
- {
- UIVertex vert = new UIVertex();
- // 处理超链接包围框
- foreach (var hrefInfo in _hyperlinkInfoList)
- {
- hrefInfo.BoxList.Clear();
- //一个字符是四个顶点,所以Index要乘以4
- int startVertex = hrefInfo.StartIndex * 4;
- int endVertex = hrefInfo.EndIndex * 4;
- if (startVertex >= toFill.currentVertCount)
- {
- continue;
- }
- // 将超链接里面的文本顶点索引坐标加入到包围框
- toFill.PopulateUIVertex(ref vert, startVertex);
- var pos = vert.position;
- var bounds = new Bounds(pos, Vector3.zero);
- for (int i = startVertex; i < endVertex; i++)
- {
- if (i >= toFill.currentVertCount)
- {
- break;
- }
- toFill.PopulateUIVertex(ref vert, i);
- pos = vert.position;
- if (pos.x < bounds.min.x) // 换行重新添加包围框
- {
- hrefInfo.BoxList.Add(new Rect(bounds.min, bounds.size));
- bounds = new Bounds(pos, Vector3.zero);
- }
- else
- {
- bounds.Encapsulate(pos); // 扩展包围框
- }
- }
- hrefInfo.BoxList.Add(new Rect(bounds.min, bounds.size));
- }
- }
- /// <summary>
- /// 获取超链接解析后的最后输出文本
- /// </summary>
- /// <returns></returns>
- private string GetOutputText(string outputText)
- {
- StringBuilder stringBuilder = new StringBuilder();
- _hyperlinkInfoList.Clear();
- int strIndex = 0;
- foreach (Match match in _hrefRegex.Matches(outputText))
- {
- string appendStr = outputText.Substring(strIndex, match.Index - strIndex);
- stringBuilder.Append(appendStr);
- //空格和回车没有顶点渲染,所以要去掉
- stringBuilder = stringBuilder.Replace(" ", "");
- stringBuilder = stringBuilder.Replace("\n", "");
- int startIndex = stringBuilder.Length;
- //第一个是连接url,第二个是连接文本,跳转用url,计算index用文本
- Group urlGroup = match.Groups[1];
- Group titleGroup = match.Groups[2];
- //如果有Color语法嵌套,则还要继续扒,知道吧最终文本扒出来
- Match colorMatch = _colorRegex.Match(titleGroup.Value);
- if (colorMatch.Groups.Count > 3)
- {
- titleGroup = colorMatch.Groups[2];
- }
- stringBuilder.Append(titleGroup.Value);
- HyperlinkInfo hyperlinkInfo = new HyperlinkInfo
- {
- StartIndex = startIndex,
- EndIndex = (startIndex + titleGroup.Length),
- Name = urlGroup.Value
- };
- strIndex = match.Index + match.Length;
- _hyperlinkInfoList.Add(hyperlinkInfo);
- }
- stringBuilder.Append(outputText.Substring(strIndex, outputText.Length - strIndex));
- return stringBuilder.ToString();
- }
- /// <summary>
- /// 添加可视包围框(测试用方法)
- /// </summary>
- private void AddVisibleBound()
- {
- int index = 0;
- foreach (var hyperLinkInfo in _hyperlinkInfoList)
- {
- Color color = new Color(UnityEngine.Random.Range(0f, 1f), UnityEngine.Random.Range(0f, 1f), UnityEngine.Random.Range(0f, 1f), 0.2f);
- index++;
- foreach (Rect rect in hyperLinkInfo.BoxList)
- {
- GameObject gameObject = new GameObject();
- gameObject.name = string.Format("GOBoundBox[{0}]", hyperLinkInfo.Name);
- gameObject.transform.SetParent(this.gameObject.transform);
- RectTransform rectTransform = gameObject.AddComponent<RectTransform>();
- rectTransform.sizeDelta = rect.size;
- rectTransform.localPosition = new Vector3(rect.position.x + rect.size.x / 2, rect.position.y + rect.size.y / 2, 0);
- Image image = gameObject.AddComponent<Image>();
- image.color = color;
- image.raycastTarget = false;
- }
- }
- }
- #endregion
- }
|