Unity UGUI超链接下划线

网络上有很多功能类似的脚本,但是大部分多多少少都有点问题,挨个copy了一点随便改了改,这个代码是优化前的代码,用的时候可以自己优化下。

UI结构:
image.png

把NewHyperlinkText.cs和TextExpand.cs这俩脚本挂在上面就可以了。
测试text="message_text<href=test1>TEST_1</href>message_text"

效果:
image.png

TextExpand.cs是从另一位大佬那里搬来的。自行搬迁。

NewHyperlinkText.cs

/*
 * text = "message_text<href=test1>TEST_1</href>message_text<href=test2>TEST_2</href>message_text"
*/
using System;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Pikayoo
{
    public class NewHyperlinkText : ShrinkText, IPointerClickHandler
    {
        public Action<string> onHyperlinkClick;

        /// <summary>
        /// 超链接信息类
        /// </summary>
        private class HyperlinkInfo
        {
            public int charStart;
            public int charEnd;
            public int colorTextLength;
            public int colorTextSpaceCnt;

            public int startIndex;

            public int endIndex;

            public string name;

            public readonly List<Rect> boxes = new List<Rect>();

            public List<int> linefeedIndexList = new List<int>();
        }

        /// <summary>
        /// 解析完最终的文本
        /// </summary>
        private string m_OutputText;

        /// <summary>
        /// 超链接信息列表
        /// </summary>
        private readonly List<HyperlinkInfo> m_HrefInfos = new List<HyperlinkInfo>();

        /// <summary>
        /// 文本构造器
        /// </summary>
        protected StringBuilder s_TextBuilder = new StringBuilder();

        /// <summary>
        /// 超链接文本颜色
        /// </summary>
        private static Color32 innerTextColor = new Color32(150, 72, 194, 255);

        //计算定点信息的缓存数组
        private readonly UIVertex[] m_TempVerts = new UIVertex[4];

        /// <summary>
        /// 超链接正则
        /// </summary>
        private static readonly Regex s_HrefRegex =
            new Regex(@"<href=([^>\n\s]+)>(.*?)(</href>)", RegexOptions.Singleline);


        public string GetHyperlinkInfo
        {
            get { return text; }
        }

        public override void SetVerticesDirty()
        {
            base.SetVerticesDirty();

            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();
                hrefInfo.linefeedIndexList.Clear();
                if (hrefInfo.startIndex >= toFill.currentVertCount)
                {
                    continue;
                }

                // 将超链接里面的文本顶点索引坐标加入到包围框
                toFill.PopulateUIVertex(ref vert, hrefInfo.startIndex);

                var pos = vert.position;
                var bounds = new Bounds(pos, Vector3.zero);
                hrefInfo.linefeedIndexList.Add(hrefInfo.startIndex);
                for (int i = hrefInfo.startIndex, m = hrefInfo.endIndex; i < m; I++)
                {
                    if (i >= toFill.currentVertCount)
                    {
                        break;
                    }

                    toFill.PopulateUIVertex(ref vert, i);
                    vert.color = innerTextColor;
                    toFill.SetUIVertex(vert, i);

                    pos = vert.position;

                    bool needEncapsulate = true;

                    if (i > 4 && (i - hrefInfo.startIndex) % 4 == 0)
                    {
                        UIVertex lastV = new UIVertex();
                        toFill.PopulateUIVertex(ref lastV, i - 4);
                        var lastPos = lastV.position;

                        if (pos.x < lastPos.x && pos.y < lastPos.y) // 换行重新添加包围框
                        {
                            hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
                            hrefInfo.linefeedIndexList.Add(i);
                            bounds = new Bounds(pos, Vector3.zero);
                            needEncapsulate = false;
                        }
                    }

                    if (needEncapsulate)
                    {
                        bounds.Encapsulate(pos); // 扩展包围框
                    }
                }

                hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
            }

            //添加下划线
            //一行行的划 文本拉伸边缘渐变 不好看
            //Vector2 extents = rectTransform.rect.size;
            //var settings = GetGenerationSettings(extents);
            //TextGenerator underlineText = new TextGenerator();
            //underlineText.Populate("_", settings);
            //IList<UIVertex> tut = underlineText.verts;
            //foreach (var hrefInfo in m_HrefInfos)
            //{
            //    if (hrefInfo.startIndex >= toFill.currentVertCount)
            //    {
            //        continue;
            //    }

            //    for (int i = 0; i < hrefInfo.boxes.Count; I++)
            //    {
            //        //计算下划线的位置
            //        Vector3[] ulPos = new Vector3[4];
            //        ulPos[0] = hrefInfo.boxes[i].position + new Vector2(0, fontSize * 0.2f);
            //        ulPos[1] = ulPos[0] + new Vector3(hrefInfo.boxes[i].width, 0.0f);
            //        ulPos[2] = hrefInfo.boxes[i].position + new Vector2(hrefInfo.boxes[i].width, 0f);
            //        ulPos[3] = hrefInfo.boxes[i].position;

            //        //绘制下划线
            //        for (int j = 0; j < 4; j++)
            //        {
            //            m_TempVerts[j] = tut[j];
            //            m_TempVerts[j].color = new Color(104, 86, 80, 255);
            //            m_TempVerts[j].position = ulPos[j];
            //        }
            //        toFill.AddUIVertexQuad(m_TempVerts);
            //    }
            //}

            // 使用组件画下划线
            var ext = GetComponent<TextExpand>();
            if (null != ext && ext.enabled)
            {
                ext.lineHeight = fontSize * 0.1f;
                ext.useUnderline = true;
                ext.lineHeightJustify = true;
                ext.useCustomLineIndexArray = true;
                ext.lineColor = innerTextColor;
                ext.customLineIndexArray = new Vector2Int[m_HrefInfos.Count];
                for (int i = 0; i < m_HrefInfos.Count; I++)
                {
                    ext.customLineIndexArray[i].x = m_HrefInfos[i].charStart;
                    ext.customLineIndexArray[i].y = m_HrefInfos[i].charEnd;
                }
            }
            else
            {
                //一个字一个字的划 效率差 而且字与字之间容易有接缝
                DrawUnderLine(toFill);
            }
        }

        private void DrawUnderLine(VertexHelper vh)
        {
            UIVertex vert = new UIVertex();
            List<Vector3> startPosList = new List<Vector3>();
            List<Vector3> endPosList = new List<Vector3>();
            foreach (var hrefInfo in m_HrefInfos)
            {
                if (hrefInfo.startIndex >= vh.currentVertCount)
                {
                    continue;
                }

                float minY = float.MaxValue;
                for (int i = hrefInfo.startIndex, m = hrefInfo.endIndex; i < m; i += 4)
                {
                    if (i >= vh.currentVertCount)
                    {
                        break;
                    }

                    if (hrefInfo.linefeedIndexList.Contains(i))
                    {
                        for (int j = 0; j < startPosList.Count; j++)
                        {
                            MeshUnderLine(vh, new Vector2(startPosList[j].x, minY), new Vector2(endPosList[j].x, minY));
                        }

                        startPosList.Clear();
                        endPosList.Clear();
                    }

                    vh.PopulateUIVertex(ref vert, i + 3);
                    startPosList.Add(vert.position);
                    vh.PopulateUIVertex(ref vert, i + 2);
                    endPosList.Add(vert.position);

                    if (vert.position.y < minY)
                    {
                        minY = vert.position.y;
                    }
                }

                for (int j = 0; j < startPosList.Count; j++)
                {
                    MeshUnderLine(vh, new Vector2(startPosList[j].x, minY), new Vector2(endPosList[j].x, minY));
                }

                startPosList.Clear();
                endPosList.Clear();
            }
        }

        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; /*new UIVertex[4];*/ //"_"的的顶点数组

            var size = fontSize;
            var posX = size * 0.15f;
            var posY = 3f;//UnityEngine.Mathf.Max(size * 0.05f, 1);
            Vector3[] pos = new Vector3[4];
            pos[0] = startPos + new Vector2(-posX, -posY);
            pos[3] = startPos + new Vector2(-posX, posY);
            pos[2] = endPos + new Vector2(posX, posY);
            pos[1] = endPos + new Vector2(posX, -posY);

            UIVertex[] tempVerts = new UIVertex[4];
            for (int i = 0; i < 4; I++)
            {
                tempVerts[i] = lineVer[i];
                tempVerts[i].color = innerTextColor;
                tempVerts[i].position = pos[I];
            }

            vh.AddUIVertexQuad(tempVerts);
        }

        /// <summary>
        /// 获取超链接解析后的最后输出文本
        /// </summary>
        /// <returns></returns>
        protected virtual string GetOutputText(string outputText)
        {
            s_TextBuilder.Length = 0;
            m_HrefInfos.Clear();
            var indexText = 0;
            int count = 0;
            foreach (Match match in s_HrefRegex.Matches(outputText))
            {
                string appendStr = outputText.Substring(indexText, match.Index - indexText);

                s_TextBuilder.Append(appendStr);

                //空格和回车没有顶点渲染,所以要去掉
                //s_TextBuilder = s_TextBuilder.Replace(" ", "");
                //s_TextBuilder = s_TextBuilder.Replace("\n", "");
                count += appendStr.Length - appendStr.Replace(" ", "").Replace("\n", "").Length;
                int startIndex = (s_TextBuilder.Length - count) * 4;
                var group = match.Groups[1];
                var textStartIndex = match.Index;
                var textLength = match.Groups[2].Length;
                var totalLen = match.Groups[0].Length;
                // 要去除掉空格,否则文字变色会有问题
                var thisSpaceLen = textLength - (match.Groups[2].ToString().Replace(" ", "")).Length;
                var spaceLen = 0;
                foreach (var item in m_HrefInfos)
                {
                    textStartIndex -= item.colorTextLength;
                    spaceLen += item.colorTextSpaceCnt;
                }
                var hrefInfo = new HyperlinkInfo
                {
                    colorTextSpaceCnt = thisSpaceLen,
                    charStart = textStartIndex,
                    colorTextLength = totalLen-textLength,
                    charEnd = textStartIndex+textLength-1,
                    startIndex = startIndex - (spaceLen * 4), // 超链接里的文本起始顶点索引
                    endIndex = startIndex + ((textLength-spaceLen-thisSpaceLen) * 4),
                    //endIndex = (s_TextBuilder.Length + match.Groups[2].Length - 1) * 4 + 3,
                    name = group.Value
                };
                m_HrefInfos.Add(hrefInfo);

                s_TextBuilder.Append(match.Groups[2].Value);
                indexText = match.Index + match.Length;
            }

            s_TextBuilder.Append(outputText.Substring(indexText, outputText.Length - indexText));
            return s_TextBuilder.ToString();
        }

        /// <summary>
        /// 点击事件检测是否点击到超链接文本
        /// </summary>
        /// <param name="eventData"></param>
        public void OnPointerClick(PointerEventData eventData)
        {
            RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position,
                eventData.pressEventCamera, out var lp);

            foreach (var hrefInfo in m_HrefInfos)
            {
                var boxes = hrefInfo.boxes;
                for (var i = 0; i < boxes.Count; ++i)
                {
                    if (boxes[i].Contains(lp))
                    {
                        if (onHyperlinkClick != null)
                        {
                            onHyperlinkClick.Invoke(hrefInfo.name);
                        }
                        //Debug.LogError("超链接信息:" + hrefInfo.name);
                        //Debug.Log("超链接信息:" + hrefInfo.name);
                        return;
                    }
                }
            }
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。