unity UGUI中获取点击位置处的URL链接

news2024/11/26 0:57:53

需求是,我们在一个text组件中像写网页那样写入链接,然后点击这个链接,就能访问配置的网页啥的。比如:

<a href="hello">链接文本</a></summary>

最终的效果如下:

图中,image区域就是各个链接的点击范围。原理是获取text中,每个字符的位置,然后算出每个链接对应的点击区域,最后返回鼠标点到的那个区域的链接。代码比较简单,就直接写点注释看吧。实现是继承了text组件,当然写成静态方法传入text来计算也可以。

比较一下网上搜到的其他方案,这个方法不用重载mesh,效率应该是比较高的。

#define TEST_CheckClickURL
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.UI;

public class TestClickURL : Text
{
    public Button button;
    public void OnButtonClick()
    {
        Debug.Log(CheckClickURL()?.url);
    }

    // 定义返回的结果
    public class CheckClickURLResult
    {
        public string url;
        public string text;
        public Rect rect;
        public CheckClickURLResult(string url, string text, Rect rect)
        {
            this.url = url;
            this.text = text;
            this.rect = rect;
        }
    }
    private static Regex hrefRegex =
        new Regex(@"<a href=([^>\n\s]+)>(.*?)(</a>)",
            RegexOptions.Singleline);
    /// <summary> 计算点击到的URL文本内容,返回网址
    /// 格式如下:<a href="hello">链接文本</a></summary>
    public CheckClickURLResult CheckClickURL()
    {
        Profiler.BeginSample("CheckClickURL");
        if (hrefRegex == null)
            hrefRegex = new Regex(
                @"<a href=([^>\n\s]+)>(.*?)(</a>)", RegexOptions.Singleline);
        InitDebugGOList();

        // 将点击位置从屏幕坐标转为本地坐标
        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            this.rectTransform, Input.mousePosition,
            null, out var mouseLocalPosition);
        //注意使用UI相机

        // 获取生成的文本数据。
        // characters 保存了每个字符左上角的位置。
        // lines 保存了每行开始字符ID,和行高。
        var generator = cachedTextGenerator;
        var charList = generator.characters;
        var lineList = generator.lines;
        var textStr = text;

        // 正则表达式查找链接文本
        var matchs = hrefRegex.Matches(textStr);
        foreach (Match match in matchs)
        {
            var urlGroup = match.Groups[1];
            var textGroup = match.Groups[0];
            var textStartIndex = textGroup.Index;
            var textEndIndex = textGroup.Index + textGroup.Length;
            // 我们的字符可能是换行的,所以要按行分割。
            // 倒着遍历就很容易获取每行开始和结束位置。
            var lineEndIndex = charList.Count - 1;
            for (int i = lineList.Count - 1; i >= 0; i--)
            {
                var lineStartIndex = lineList[i].startCharIdx;
                // 处理换行后的截取
                var realStart = Mathf.Max(lineStartIndex, textStartIndex);
                var realEnd = Mathf.Min(lineEndIndex, textEndIndex);
                // 本行没有链接内容的情况
                if (realStart > realEnd) continue;
                // 问题简化成单行的点击检查,提个函数继续处理。
                var result = CheckLine(realStart, realEnd, lineList[i].height, mouseLocalPosition, out var rect);
                if (result) return new CheckClickURLResult(urlGroup.Value, textGroup.Value, rect);

                //Debug.Log($"{start}/{end}");
                lineEndIndex = lineStartIndex - 1;
            }
        }
        Profiler.EndSample();
        return null;
    }

    public bool CheckLine(int start, int end, float lineHeight, Vector2 mouseLocalPosition, out Rect rect)
    {
        // 获取生成的文本数据。
        var charList = cachedTextGenerator.characters;
        var startPoint = charList[start].cursorPos;
        var endPoint = charList[end].cursorPos;

        // 直接计算出本行中链接可点击区域。
        var x = startPoint.x;
        var y = startPoint.y - lineHeight;
        var width = endPoint.x - startPoint.x;
        var height = lineHeight;
        rect = new Rect(x, y, width, height);

        var result = rect.Contains(mouseLocalPosition);
        CreateDebugImage(rect, result);
        return result;
    }

#if TEST_CheckClickURL
    // 测试用。生成空image展示出点击判定范围。
    public static List<GameObject> debugGOList;
    public void CreateDebugImage(Rect rect, bool contains)
    {
        Debug.Log($"rect={rect}");
        var go = new GameObject("DebugImage",
            typeof(RectTransform), typeof(Image));
        debugGOList.Add(go);
        var rtf = go.GetComponent<RectTransform>();
        rtf.SetParent(transform);
        rtf.pivot = Vector2.zero;
        rtf.anchorMin = Vector2.one / 2;
        rtf.anchorMax = Vector2.one / 2;
        rtf.sizeDelta = rect.size;
        rtf.localScale = Vector3.one;
        rtf.rotation = Quaternion.identity;
        rtf.anchoredPosition = rect.position - rectTransform.rect.center;
        // 点击到的那个范围展示为红色。
        if (contains)
            go.GetComponent<Image>().color = Color.red;
    }
    public void InitDebugGOList()
    {
        if (debugGOList == null)
            debugGOList = new List<GameObject>();
        debugGOList.ForEach(p => Destroy(p));
    }
#else
        public void CreateDebugImage(Rect rect, bool contains) { }
        public void InitDebugGOList() { }
#endif
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1260177.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

血的教训------入侵redis之利用python来破解redis密码

血的教训------入侵redis之利用python来破解redis密码 利用强大的python来进行redis的密码破解&#xff0c;过程不亦乐乎&#xff0c;当然也可以用shell脚本 本篇文章只供学习交流&#xff0c;请勿他用&#xff0c;谢谢。 其他相关联的文章 [1]VMware安装部署kail镜像服务器【…

WPS Office JS宏实现批量处理Word中的表格样式

由于本职工作原因&#xff0c;经常会用到office办公软件&#xff0c;经常很多内容审批后&#xff0c;需要统一修改内容或样式&#xff0c;如果Word文档中有上百页或上千页&#xff0c;则一个一个修改太麻烦了。 在接触到WPSJS宏后&#xff0c;发现工作效率大大提升&#xff1b;…

Python字典合并

合并两个有部分key相同的字典&#xff0c;相同key保留两个字典中对应key的较大值。 (笔记模板由python脚本于2023年11月27日 18:12:15创建&#xff0c;本篇笔记适合熟悉Python字典的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Fr…

Rust内存布局

题图忘了来自哪里.. 整型,浮点型,struct,vec!,enum 本文是对 Rust内存布局 的学习与记录 struct A { a: i64, b: u64,}struct B { a: i32, b: u64,}struct C { a: i64, b: u64, c: i32,}struct D { a: i32, b: u64, c: i32, d: u64,}fn main(…

eclipse - jee 建立项目后没有 web.xml

eclipse -- jee 建立项目后没有 web.xml 处理它的方法是&#xff0c;点 File - New - Dynamic Web Project , 此时起一个项目名如M4 然后next 然后next 出现如此所示:

【办公类-18-03】(Python)教师研讨表批量制作(学校、姓名、回答1-3)

背景需求&#xff1a; 领导发给我一个word版本的“研讨表”&#xff1a; “随便你做成什么样子&#xff0c;最后能有个二维码给老师们填写反馈就可以了” 我看了看内容&#xff0c;这和我以前做的“闵行区教师信息技术2.0培训作业”完全相同 “OK&#xff0c;我用问卷星收集教…

第二节HarmonyOS DevEco Studio创建项目以及界面认识

一、创建项目 如果你是首次打开DevEco Studio&#xff0c;那么首先会进入欢迎页。 在欢迎页中单击Create Project&#xff0c;进入项目创建页面。 选择‘Application’&#xff0c;然后选择‘Empty Ability’&#xff0c;单击‘Next’进入工程配置页。 配置页中&#xff0c;详…

【Java并发】聊聊不安全的HashMap以及ConcurrentHashMap

在实际的开发中&#xff0c;hashmap是比较常用的数据结构&#xff0c;如果所开发的系统并发量不高&#xff0c;那么没有问题&#xff0c;但是一旦系统的并发量增加一倍&#xff0c;那么就可能出现不可控的系统问题&#xff0c;所以在平时的开发中&#xff0c;我们除了需要考虑正…

深度学习中小知识点系列(六) 解读SPP / SPPF / SimSPPF / ASPP / RFB / SPPCSPC

SPP与SPPF 一、SPP的应用的背景 在卷积神经网络中我们经常看到固定输入的设计&#xff0c;但是如果我们输入的不能是固定尺寸的该怎么办呢&#xff1f; 通常来说&#xff0c;我们有以下几种方法&#xff1a; &#xff08;1&#xff09;对输入进行resize操作&#xff0c;让他…

跨标签页通信的8种方式(下)

跨标签页通信是指在浏览器中的不同标签页之间进行数据传递和通信的过程。在传统的Web开发中&#xff0c;每个标签页都是相互独立的&#xff0c;无法直接共享数据。然而&#xff0c;有时候我们需要在不同的标签页之间进行数据共享或者实现一些协同操作&#xff0c;这就需要使用跨…

算法刷题-动态规划3(未完待续---------

算法刷题-动态规划3&#xff09; 01背包问题最后一块石头的重量 01背包问题 一篇文章吃透背包问题 大佬讲解什么是背包问题 问题分析&#xff1a; 面对这么多的物品&#xff0c; 选择一个个地来装入背包&#xff0c;背包的承重量不断地增加&#xff0c;二维数组中&#xff0c;…

Linux系统编程:文件系统总结

目录和文件 获取文件属性 获取文件属性有如下的系统调用&#xff0c;下面逐个来分析。 stat:通过文件路径获取属性&#xff0c;面对符号链接文件时获取的是所指向的目标文件的属性 从上图中可以看到stat函数接收一个文件的路径字符串&#xff08;你要获取哪个文件的属性&a…

ubuntu 下载编译 opencv4.2.0并检验

如有帮助点赞收藏关注&#xff01; 如需转载&#xff0c;请注明出处&#xff01; ubuntu 的opencv4.2.0下载与编译 下载依赖开始编译安装配置OpenCV编译环境检验* 完成 下载 首先下载opencv源码网址&#xff1a; https://opencv.org/releases/page/3/ 下载成zip后&#xff0c;…

GPIO的使用--操作PF09 PF10 PF08实现呼吸灯、跑马灯、警报闪烁灯

先来个呼吸灯演示 呼吸灯 目录 一、GPIO的介绍 1.含义 2.控制原理 3.控制流程 二、LED控制 1.呼吸灯 操作代码 烧录结果 2.蜂鸣器红绿灯交替 操作代码 3.红绿灯交替闪烁 操作代码 一、GPIO的介绍 1.含义 GPIO(general porpose intput output),通用输入输出端口。…

Java游戏 王者荣耀

GameFrame类 所需图片&#xff1a; package 王者荣耀;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList…

分布式事务-两阶段提交2PC

2PC协议就是两阶段提交&#xff0c;用来解决分布式事务&#xff0c;分为两个阶段&#xff0c;分别为Prepare和Commit&#xff0c;也是PC由来。 第一阶段Prepare 提交事务请求 如图所示&#xff0c;主要流程有以下三个方面 询问&#xff1a;事务协调者(Manager)向所有的事务参与…

SpringBoot : ch06 整合 web(二)

前言 SpringBoot作为一款优秀的框架&#xff0c;不仅提供了快速开发的能力&#xff0c;同时也提供了丰富的文档和示例&#xff0c;让开发者更加容易上手。在本博客中&#xff0c;我们将介绍如何使用SpringBoot来整合Web应用程序的相关技术&#xff0c;并通过实例代码来演示如何…

C++值常用集合算法

C值常用集合算法 set_intersection #include<iostream> using namespace std; #include<vector> #include<numeric> #include<algorithm>class MyPrint { public:void operator()(int val){cout << val<<" ";} };void test() {v…

电力感知边缘计算技术网关产品设计方案-硬件方案

网关硬件架构设计图: 1.配置方案 配置差异 A类网关 B类网关 CPU

Postman接口测试 —— 设置断言和集合运行

一、常见的5种断言方法 Postman是一款非常强大的API接口调式工具&#xff0c;它自带断言方法&#xff0c;不需要学习JavaScript脚本&#xff0c;非常方便。 &#xff08;1&#xff09;Status code&#xff1a;Code is 200(校验接口返回结果的状态码) &#xff08;2&#xff09…