Unity超链接:支持点击事件,下划线以及自定义颜色

时间:2025-01-23 07:40:27
/* /zyf2533/article/details/122703640 */ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using UnityEngine; using UnityEngine.EventSystems; //using ; namespace UnityEngine.UI { [AddComponentMenu("UI/UIHyperlinkText")] /// <summary> /// eg: /// 获得了:<a href="Tip#1102201">先锋盾合成卷轴</a> /// </summary> public class UIHyperlinkText : Text, IPointerClickHandler { /// <summary> /// 超链接信息类 /// </summary> private class HyperlinkInfo { //起始Index public int StartIndex; //结束Index public int EndIndex; //内容 public string RefValue; public string InnerValue; //包围框 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 static Regex colorPreRegex = new Regex(@"<color=([^>\n\s]+)>", RegexOptions.Singleline); private static Regex colorEndRegex = new Regex(@"</color>", RegexOptions.Singleline); //超链接信息列表 private List<HyperlinkInfo> hyperlinkInfoList = new List<HyperlinkInfo>(); private static Action<string, string> clickCallback = null; private static Color innerTextColor = Color.blue; #endregion #region 公有变量 #endregion #region 生命周期 protected override void OnPopulateMesh(VertexHelper toFill) { base.OnPopulateMesh(toFill); InitHyperlinkInfo(); InitHyperlinkBox(toFill); DrawUnderLine(toFill); } #endregion #region 公有方法 #endregion #region 动作 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)) { if (clickCallback != null) { clickCallback(hyperlinkInfo.RefValue, hyperlinkInfo.InnerValue); } return; } } } } public static void RegisterClickCallback(Action<string, string> callback, string innerColor) { clickCallback = callback; if (!innerColor.StartsWith("#")) { innerColor = "#" + innerColor; } Color nowColor; ColorUtility.TryParseHtmlString(innerColor, out nowColor); innerTextColor = nowColor; } #endregion #region 私有方法 /// <summary> /// 初始化连接信息 /// </summary> private void InitHyperlinkInfo() { 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); //(vert, i); pos = vert.position; bool needEncapsulate = true; if ((i - startVertex) % 4 == 0) { if (i < 4) continue; UIVertex lastV = new UIVertex(); toFill.PopulateUIVertex(ref lastV, i - 4); var lastPos = lastV.position; if (pos.x < lastPos.x && pos.y < lastPos.y) // 换行重新添加包围框 { hrefInfo.BoxList.Add(new Rect(bounds.min, bounds.size)); bounds = new Bounds(pos, Vector3.zero); needEncapsulate = false; } } if (needEncapsulate) { bounds.Encapsulate(pos); // 扩展包围框 } } hrefInfo.BoxList.Add(new Rect(bounds.min, bounds.size)); } } private void DrawUnderLine(VertexHelper vh) { foreach (var link in hyperlinkInfoList) { foreach (var rect in link.BoxList) { float height = rect.height; // 左下 var pos1 = new Vector3(rect.min.x, rect.min.y, 0); // 右下 var pos2 = new Vector3(rect.max.x, rect.max.y, 0) - new Vector3(0, height, 0); MeshUnderLine(vh, pos1, pos2); } } } private void MeshUnderLine(VertexHelper vh, Vector2 startPos, Vector2 endPos) { Vector2 extents = rectTransform.rect.size; var setting = GetGenerationSettings(extents); TextGenerator underlineText = new TextGenerator(); underlineText.Populate(" ̄", setting); IList<UIVertex> lineVer = underlineText.verts; //" ̄"的的顶点数组 Vector3[] pos = new Vector3[4]; pos[0] = startPos + new Vector2(-8, 0); pos[3] = startPos + new Vector2(-8, -4f); pos[2] = endPos + new Vector2(8, -4f); pos[1] = endPos + new Vector2(8, 0); if (lineVer.Count == 4) { UIVertex[] tempVerts = new UIVertex[4]; for (int i = 0; i < 4; i++) { tempVerts[i] = lineVer[i]; tempVerts[i].color = Color.white; tempVerts[i].position = pos[i]; tempVerts[i].uv0 = lineVer[i].uv0; tempVerts[i].uv1 = lineVer[i].uv1; tempVerts[i].uv2 = lineVer[i].uv2; tempVerts[i].uv3 = lineVer[i].uv3; } vh.AddUIVertexQuad(tempVerts); } } /// <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); //空格和回车没有顶点渲染,所以要去掉 appendStr = appendStr.Replace(" ", ""); appendStr = appendStr.Replace("\n", ""); appendStr = RichStringFilter(appendStr); stringBuilder.Append(appendStr); 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]; } var inner = titleGroup.Value; inner = inner.Replace(" ", ""); inner = inner.Replace("\n", ""); stringBuilder.Append(inner); HyperlinkInfo hyperlinkInfo = new HyperlinkInfo { StartIndex = startIndex, EndIndex = (startIndex + inner.Length), RefValue = urlGroup.Value, InnerValue = titleGroup.Value, }; strIndex = match.Index + match.Length; hyperlinkInfoList.Add(hyperlinkInfo); } stringBuilder.Append(outputText.Substring(strIndex, outputText.Length - strIndex)); return stringBuilder.ToString(); } private string RichStringFilter(string outputText) { outputText = colorPreRegex.Replace(outputText, ""); outputText = colorEndRegex.Replace(outputText, ""); return outputText; } #endregion /// <summary> /// 添加可视包围框(测试用方法) /// </summary> private void AddVisibleBound() { int index = 0; foreach (Transform item in this.gameObject.transform.transform) { Destroy(item.gameObject); } 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.InnerValue); 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; } } } } }