Unity中UGUI的Text实现超链接点击的解决方案

2023-11-17

功能简介:

1、同一个Text内可以实现多个不同字符区域的点击;
2、适配了中文、英文、韩文、日文、阿拉伯语等,更多语种待测试;

C#脚本:

/******************************************************************
** 文件名:  UIText_Link.cs
** 版  权:  (C)  
** 创建人:  Summer
** 日  期:  2022/3/2
** 描  述:  超链接文本         
*
    "<a i=id>%s</a>"
*******************************************************************/
using System;
using UnityEngine;
using System.Text;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class Text_Link : Text, IPointerClickHandler
{
    protected override void Awake()
    {
        Set_TextLinkFuncCB((string i) =>
        {
            Debug.Log($"点击了:{i}");
        });
    }
    /// <summary>
    /// 解析完最终的文本
    /// </summary>
    private string m_OutputText;

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

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

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

    //
    private static readonly Regex s_VertexFilter = new Regex(@"(|[ \n\r\t]+)", RegexOptions.Singleline);

    VertexHelper _toFill = null;
    /// <summary>
    /// 是否使用超链接  默认未False
    /// </summary>
    bool bool_IsLink = false;

    private Action<string> linkFunc_Cb = null;

    private RectTransform rect_Parent;
    private RectTransform Rect_Parent
    {
        get
        {
            if (rect_Parent == null)
            {
                Transform trans = this.transform.parent != null ? this.transform.parent.transform : this.transform;
                rect_Parent = trans.GetComponent<RectTransform>();
            }

            return rect_Parent;
        }
    }

    //设置 文本 超链接的点击回调事件
    public void Set_TextLinkFuncCB(Action<string> linkFunc_Cb)
    {
        bool_IsLink = true;
        if (this.linkFunc_Cb != null)
        {
            this.linkFunc_Cb = null;
        }
        this.linkFunc_Cb = linkFunc_Cb;
        OnPopulateMesh(_toFill);
    }

    //字符顶点数
    const int perCharVerCount = 4;

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

    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        if (toFill == null)
        {
            return;
        }
        _toFill = toFill;
        //TODO 编辑器状态下这里不执行, 方便调试看到效果用
        if (!bool_IsLink)
        {
            m_Text = GetOutputText_Nomal(text);
            base.OnPopulateMesh(toFill);
            return;
        }

        var orignText = m_Text;
        m_OutputText = GetOutputText_Init(text);
        m_Text = m_OutputText;
        text = m_OutputText;
        base.OnPopulateMesh(toFill);
        m_Text = orignText;
        GetOutputText(text, toFill.currentVertCount);

        UIVertex vert = new UIVertex();

        // 处理超链接包围框
        foreach (var hrefInfo in m_HrefInfos)
        {
            hrefInfo.boxes.Clear();
            if (hrefInfo.startIndex >= toFill.currentVertCount)
            {
                continue;
            }

            // 将超链接里面的文本顶点索引坐标加入到包围框
            toFill.PopulateUIVertex(ref vert, hrefInfo.startIndex);
            var pos = vert.position;
            var bounds = new Bounds(pos, Vector3.zero);
            Vector3 previousPos = Vector3.zero;
            for (int i = hrefInfo.startIndex, m = hrefInfo.endIndex; i < m; i++)
            {
                if (i >= toFill.currentVertCount)
                {
                    break;
                }

                toFill.PopulateUIVertex(ref vert, i);
                pos = vert.position;
                if ((i - hrefInfo.startIndex) % 4 == 1)
                {
                    previousPos = pos;
                }
                if (previousPos != Vector3.zero && (i - hrefInfo.startIndex) % 4 == 0 && pos.x < previousPos.x) // 换行重新添加包围框
                {
                    hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
                    bounds = new Bounds(pos, Vector3.zero);
                }
                else
                {
                    bounds.Encapsulate(pos); // 扩展包围框
                }
            }
            hrefInfo.boxes.Add(new Rect(bounds.min, bounds.size));
        }


        if (this.gameObject.activeInHierarchy)
        {
            StartCoroutine(RefrehLayout());
        }
    }

    IEnumerator RefrehLayout()
    {
        yield return new WaitForEndOfFrame();
        LayoutRebuilder.ForceRebuildLayoutImmediate(Rect_Parent);
    }

    //初始化超链接文本 获取最终结果的定点数用
    string GetOutputText_Init(string outputText)
    {
        s_TextBuilder.Length = 0;
        m_HrefInfos.Clear();
        var indexText = 0;
        foreach (Match match in s_HrefRegex.Matches(outputText))
        {
            s_TextBuilder.Append(outputText.Substring(indexText, match.Index - indexText));
            s_TextBuilder.Append("<i><b><color=#f49037>");  // 超链接颜色

            s_TextBuilder.Append(match.Groups[2].Value);
            s_TextBuilder.Append("</color></b></i>");
            indexText = match.Index + match.Length;
        }
        s_TextBuilder.Append(outputText.Substring(indexText, outputText.Length - indexText));
        return s_TextBuilder.ToString();
    }

    /// <summary>
    /// 获取超链接解析后的最后输出文本
    /// </summary>
    /// <returns></returns>
    string GetOutputText(string outputText, int currentVertCount)
    {
        s_TextBuilder.Length = 0;
        m_HrefInfos.Clear();
        var indexText = 0;
        int vertCount = Regex.Replace(Regex.Replace(outputText.ToString(), @"\s", ""), @"<(.*?)>", "").Length * 4;
        int vercCount_Offset_Start = 0;
        int vercCount_Offset_End = 0;
        bool isLineCup = false;
        if (currentVertCount > vertCount)
        {
            isLineCup = true;
            vercCount_Offset_Start = 80;
            vercCount_Offset_End = 88;
        }
        foreach (Match match in s_HrefRegex.Matches(outputText))
        {
            s_TextBuilder.Append(outputText.Substring(indexText, match.Index - indexText));
            int offset_Len = 0;
            if (isLineCup)
            {
                offset_Len = (s_TextBuilder.Length - Regex.Replace(s_TextBuilder.ToString(), @"<(.*?)>", "").Length) * 4;
            }

            s_TextBuilder.Append("<i><b><color=#f49037>");  // 超链接颜色

            var str = Regex.Replace(s_TextBuilder.ToString(), @"\s", "");
            var group = match.Groups[1];
            var hrefInfo = new HrefInfo_
            {
                startIndex = Regex.Replace(str, @"<(.*?)>", "").Length * 4 + vercCount_Offset_Start + offset_Len, // 超链接里的文本起始顶点索引
                endIndex = (Regex.Replace(str, @"<(.*?)>", "").Length +
                Regex.Replace(Regex.Replace(match.Groups[2].ToString(), @"\s", "")
                , @"<(.*?)>", "").Length - 1) * 4 + 3 + vercCount_Offset_End + offset_Len,
                name = group.Value
            };
            m_HrefInfos.Add(hrefInfo);
            //Debug.Log($"顶点信息,开始的:{hrefInfo.startIndex},结束的:{hrefInfo.endIndex}");

            s_TextBuilder.Append(match.Groups[2].Value);
            s_TextBuilder.Append("</color></b></i>");
            indexText = match.Index + match.Length;
        }

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

    //获取祛除掉超链接 保留普通文本  保证配置里超链接标签的文本  在其他地方也可以正常使用,只有调用了超链接初始化的  才会给超链接形式的文本
    string GetOutputText_Nomal(string outputText)
    {
        s_TextBuilder.Length = 0;
        m_HrefInfos.Clear();
        var indexText = 0;
        MatchCollection matchs = s_HrefRegex.Matches(outputText);
        if (matchs.Count <= 0)
        {
            return outputText;
        }
        foreach (Match match in matchs)
        {
            s_TextBuilder.Append(outputText.Substring(indexText, match.Index - indexText));
            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();
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        Vector2 lp;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            rectTransform, eventData.position, eventData.pressEventCamera, out lp);

        foreach (var hrefInfo in m_HrefInfos)
        {
            var boxes = hrefInfo.boxes;
            for (var i = 0; i < boxes.Count; ++i)
            {
                if (boxes[i].Contains(lp))
                {
                    //Debug.Log("技能 超链接 点击了:" + hrefInfo.name);
                    linkFunc_Cb?.Invoke(hrefInfo.name);
                    return;
                }
            }
        }
    }
}
/// <summary>
/// 超链接信息类
/// </summary>
class HrefInfo_
{
    public int startIndex;

    public int endIndex;

    public string name;

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

使用方法

1、文本框内使用富文本,见截图

2、要使该文本的超链接功能生效,调用即可:

        Set_TextLinkFuncCB((string i) =>
        {
            Debug.Log($"点击了:{i}");
        });

可以设置回调的灵活性就比较高了,各位自己扩展

Demo工程内截图

在这里插入图片描述

Demo地址:

https://github.com/Panda0000000000000/TextLink.git

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity中UGUI的Text实现超链接点击的解决方案 的相关文章

随机推荐

  • MOS管使用的条件

  • delphi JSONObject 助手 uSZHN_JSON,SuperObject语法

    Helper实现的类似SuperObject的写法 使用这个方法 需要引入单元uSZHN JSON pas 访问密码 168168 procedure TForm1 Button1Click Sender TObject var jo TJ
  • Hypervisor介绍及在智能驾驶的应用

    转自Hypervisor 智能座舱和智能驾驶融合的关键技术 腾讯新闻
  • 2023年计算机专业毕业设计选题有哪些?(附源码)

    计算机毕业设计这个选题的话其实有很多的 就看你自己能接受怎么样的 比如可以做网站类 系统类 小程序类 安卓app 大数据类等等 这个也要看你个人能力和技术问题 如果技术小白或者有一点点基础的话建议选择网站类和系统类的 如果有扎实的基础技能可
  • 【编程笔试】美团2021校招笔试-通用编程题第10场(附思路及C++代码)

    导览 练习地址 淘汰分数 正则序列 公司食堂 最优二叉树II 练习地址 点此前往练习 淘汰分数 某比赛已经进入了淘汰赛阶段 已知共有n名选手参与了此阶段比赛 他们的得分分别是a 1 a 2 a n 小美作为比赛的裁判希望设定一个分数线m 使
  • 过压保护芯片,高输入电压(OVP)

    PW2606是一种前端过电压和过电流保护装置 它实现了广泛的输入电压范围从2 5V到40V 过电压阈值可在外部编程或设置为内部默认设置 集成功率路径nFET开关的超低电阻确保了更好的性能电池充电系统应用的性能 它可以提供高达2A的电流 以满
  • python random.randint(0、2)_如何使用python random模块中的randint()函数?

    python中的random模块用于生成随机数 而要生成随机整数则需要用到 random模块里的randint 函数 randint 函数随机产生括号里两个参数之间的整数 且包括这两个参数 划定随机生成整数的范围 最小最大值 1 rando
  • ESP8266使用AT指令连接MQTT服务器

    注 不同的AT固件在使用时存在差异 此处使用的是安信可官方提供的MQTT透传AT固件 固件号 1471 本文为学习记录 仅作参考 如有不对之处 请留言 附 固件地址 AT固件汇总 安信可科技 ai thinker com https doc
  • JSON.parse()方法

    一 JSON的解析方法有两种 eval 和JSON parse var jsonstr str1 Hello str2 world var evalJson eval jsonstr var jsonParseJson JSON parse
  • python获取矩阵某一列元素

    今天写线性回归问题的时候遇到了一个问题 对于一个二维矩阵 python如何遍历其某一列元素 遍历一行是很简单的 直接使用索引即可 但是遍历一列呢 方法一 直接遍历法 使用一个循环 对每一行单独找出这列上的对应元素 方法二 列表解析法 这个方
  • webpack 常用配置

    环境搭建 既然是前端项目 那么就npm就必不可少 运行 npm install webpack cli g 来全局安装脚手架 随后使用 npm install webpack cli D 安装到工程下 在工程目录下创建 webpack co
  • stm32专题二十六:输入捕获 脉宽测量

    硬件设计 脉宽测量原理 当按键没有按下时 由硬件电路自动拉低 如果按键按下 则PA0产生上升沿 我们进行脉宽测量时 首先设置为上升沿捕获 然后在第一次捕获到上升沿时 将计数器清零 并设置捕获极性为下降沿捕获 当第二次捕获到信号时 就是一个完
  • Vigenere密码

    Vigenere密码技术 在单字符单表替换密码中 明文中的字符与密文中字符是一一对应的 在明文表中的字符也存在于在密文表中 明文中字符的统计特性在密文中没有得到改变 单字符多表替换密码技术是用一系列 两个以上 替换表依次对明文的字符进行替换
  • 跟据后台数据生成饼图及柱形图实例

    在做社团管理系统时 为了进一步完善功能 用jfreechar做了一个饼图与柱形图 做的比较粗糙 效果如下 现在跟大家分享一下实现方法 生成饼图 param list 传入参数为一个list list中包含多个map 这个list是从数据库中
  • ZooKeeper系统模型之集群间消息通信。

    ZooKeeper的消息类型大体上可以分为四类 分别是 数据同步型 服务器初始化型 请求处理型和会话管理型 数据同步型 数据同步型消息是指在Learner和Leader服务器进行数据同步的时候 网络通信所用到的消息 通常有DIFF TRUN
  • java 仓库管理_Java仓库管理系统(一)

    从小到大没有写日记的习惯 但本着互联网开放 共享的原则 并且马士兵老师曾说 当你学会一些技能的时候 看到别人正被你会的东西所困扰 你应该去帮助他 所以把仓库管理系统的详解记录一下 说的可能不那么专业 但基本都能听懂 本人编程起步 有错误请指
  • 常见手机快充协议介绍

    早在几年前 各厂家就在逐步推进手机的充电速度 随着QC PD等一系列的充电协议不断更新 各家手机厂家都有自己的快充解决方案 不仅功率不一样 手机间的充电协议与标准也变得越发复杂 不同品牌的手机快充名称也各有不同 所以我们需要先清楚我们的手机
  • 垃圾箱清空后数据恢复(亲测有效)

    辛辛苦苦写PPT写了两周 误删了还没发现 然后顺便把垃圾箱也清空了 wps还因为云空间已满不能同步上传 连个记录都没有 真的是差点气到心梗 经历了一个小时的百度 恢复了我的PPT 记录一下数据的恢复过程 无图 恢复的时候过于烦躁 忘记截图
  • STM32单片机IAP介绍

    1 什么是IAP 首先区分下两个概念 ISP和IAP ISP In System Programming 在系统中编程 通过芯片专用的串行编程接口对其内部的程序存储器进行擦写 IAP In Application Programming 在
  • Unity中UGUI的Text实现超链接点击的解决方案

    Unity实现超链接点击 功能简介 C 脚本 使用方法 Demo工程内截图 Demo地址 功能简介 1 同一个Text内可以实现多个不同字符区域的点击 2 适配了中文 英文 韩文 日文 阿拉伯语等 更多语种待测试 C 脚本 文件名 UITe