之前用的是https://github.com/akof1314/uGUI_LinkImageText这个控件,结果发现点击事件是不对的。我将包围框显示出来后它是这样一种情况
从图片可以看出这个包围框是混乱的,那就难怪点击事件不准了。经排查发现是包围框的起始和结束的Index计算有错误。
详情是这样的,这个功能的本质是截取网址这一段,拿到他的开始和结束的Index,然后遍历Text渲染顶点的时候以开始Index为左,结束Index为右,生成一个包围框(有换行另处理)。那么这个包围框就是我们点击网址的相应区域。
而原版在计算这个Index的时候出现了失误,因为忽略了两个关键点:
- 富文本的语法代码没有渲染顶点,需剔除Index计算
- 空格、回车没有渲染顶点,需剔除Index计算
也就是说,对于:
Baidu 网址: Baidu网址 google 网址: google网址 Baidu URL: https://www.baidu.com/ google URL: https://www.google.com/
这样的文本,我们需要剔除富文本html语法和空格、换行符后才能用来计算Index,就像这样:
Baidu网址:Baidu网址google 网址:google网址BaiduURLhttps://www.baidu.com/googleURL:https://www.google.com/
把所有不参与顶点计算的元素去掉才行。
如果想要改颜色,根据语法需要加标签,如:
Baidu 网址:Baidu网址 google 网址: google网址 Baidu URL: https://www.baidu.com/ google URL: https://www.google.com/
其中第一条的baidu网址显示就是红色的,当然,在计算Index的时候也要把标签刨除掉
显示如图,可以看到下面的包围框显示是完全正常的
完整代码:
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; ////// TextHyperlink 支持Unity超链接富文本语法 /// 例如https://aws.amazon.com/privacy/ /// 带颜色的超链接: /// public class TextHyperlink : Text, IPointerClickHandler { ///https://aws.amazon.com/privacy/ ////// 超链接信息类 /// private class HyperlinkInfo { //起始Index public int StartIndex; //结束Index public int EndIndex; //内容 public string Name; //包围框 public ListBoxList = new List (); } #region 私有变量 //超链接正则 private static Regex _hrefRegex = new Regex(@"ns]+)>(.*?)()", RegexOptions.Singleline); //颜色正则 private static Regex _colorRegex = new Regex(@" ns]+)>(.*?)( )", RegexOptions.Singleline); //超链接信息列表 private List_hyperlinkInfoList = new List (); //显示包围盒范围,想用的话输入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 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); return; } } } } #endregion #region 私有方法 /// /// 初始化连接信息 /// private void InitHyperlinkInfo() { //这个值不用,就是个存根。要的是执行后面那个方法 string outputText = GetOutputText(text); } ////// 初始化连接包围框 /// /// 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)); } } ////// 获取超链接解析后的最后输出文本 /// ///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(); } /// /// 添加可视包围框(测试用方法) /// 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.SetParent(this.gameObject); RectTransform rectTransform = gameObject.AddComponent(); 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.color = color; image.raycastTarget = false; } } } #endregion }



