Unity TMP Inputfield 输入框 框选 富文本 获取真实定位

news2024/11/27 13:52:00

一、带富文本标签的框选是什么

UGUI的InputField提供了selectionAnchorPosition和selectionFocusPosition,开始选择时的光标下标和当前光标下标

对于未添加富文本标签时,直接通过以上两个值,判断一下框选方向(前向后/后向前),进而对inputfield的text内容进行字符串拆分即可

相关基础内容可以看一些参考博客

https://blog.51cto.com/u_15296378/7884559

但是对于有富文本标签的inputfield,这两个值,返回的只是表面看起来的索引,并没有包含富文本标签。

举个栗子:

对于一段普通文本(未添加富文本):今天没有下雨

你想选择“没有下”,那么索引应该是,2和4

但是对于一段富文本内容:今天<b>没有</b>下雨

你想选择“没有下”,那么真实索引应该是,5和12,

那么根据实际索引,拆分后的字符串是这样“没有</b>下

然而你再使用selectionAnchorPosition和selectionFocusPosition尝试获取时,依然得到的是2和4,

那么根据原索引,拆分后的字符串是这样“<b>”,就完全被富文本标签干扰了

截止目前还没有或者我没找到官方直接可以使用的,支持带有富文本标签的,框选或选中内容定位获取接口。因此只能自己计算

二、获取真实富文本标签定位的大致思路

为了获取真实定位,这里我们先不考虑框选,先只考虑一个字符的位置。

还是以今天<b>没有</b>下雨为例

我想获取“没”的真实定位,只需要在原索引加上其前面富文本标签“<b>”的长度即可

那么2就变成了5

进而扩展一下,今天<b><i><color=yellow>没有</color></i></b>下雨 ,对于这个文本

想获取“没”的真实定位,就需要将“没”前面所有富文本标签的长度都加上

那么写一个方法,去除目标索引前的第一个富文本标签,并记录去除后的字符串和被去除标签的字符数量,然后递归调用自己,直到没有富文本标签之后结束

计算框选时第二个字的定位时,也是同理(这里后面框选时,计算的调节稍有不同,下文讲)

如果想要返回的内容中,不包含富文本标签,类似上图中的打印结果,只要“车10辆”三个字,那么使用一个正则字符串替换即可

//移除选定部分,所有富文本标签
    public string RemoveRichTextTags(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<.*?>", string.Empty);
    }

三、代码实现

在unity创建RichTextTagHandler类

这里先假定,只有一个InputField被编辑,无切换

先创建一个全局变量,用来保存某次计算的真实索引

private int total = 0;

创建RemoveFrontRichTag方法,用于移除,指定字符串,目标索引前,第一个富文本标签

/// <summary>
/// 从前往后,移除第一个富文本标签,计数标签字符数量,并将移除后的字符串返回
/// </summary>
/// <param name="aimString">待移除富文本标签的目标字符串</param>
/// <param name="surfaceIndex">原定位</param>
/// <param name="count">用来计数用的, 标记已经移除的富文本标签字符长度</param>
/// <returns>阶段性返回当前移除的富文本标签字符数量,递归后最终返回所有符合要求的富文本标签字符总数量</returns>
private string RemoveFrontRichTag(string aimString,int surfaceIndex, ref int count) {
    //先尝试定位'<'
    int meet = aimString.IndexOf('<');

    //如果定位不到,或者定位超过了原支付长度,说明标签在我们所选字符后面或者无富文本标签了,方法直接返回,并将数量count设置为0
    if (meet == -1 || meet > surfaceIndex) {
        count = 0;
        return aimString;
    }

    //成功定位到‘<’后,继续定位‘>’
    int leave = aimString.IndexOf('>');

    //将字符串拆分成去掉<>及其内部内容
    string newString = aimString.Substring(0, meet) + aimString.Substring(leave + 1, aimString.Length - leave - 1);

    //计算去掉部分的字符数量
    int length = leave - meet + 1;
    count += length;

    //Debug.Log(newString + " meet=" + meet + " leave=" + leave + " length=" + length);

    //把去掉已计数的标签后的字符串,返回
    return newString;
}

创建TryGetRealIndex方法,用于递归调用,移除目标索引前的所有富文本标签

/// <summary>
/// 尝试获取真实索引,递归方法,
/// </summary>
/// <param name="richText"></param>
/// <param name="surfaceIndex"></param>
/// <param name="isEndPoint"></param>
/// <returns></returns>
private int TryGetRealIndex(string richText, int surfaceIndex,bool isEndPoint) {

    string newString = richText;
    int count = 0;

    //先尝试定位‘<’,并获取它的定位
    int mark = newString.IndexOf('<');

    //-1是没找到,没找到或大于,原本的定位,那么代表,原本定位之前已经没有'<'了,直接返回结束

    if ((isEndPoint && (mark == -1 || mark >= surfaceIndex)) || //如果是结尾的点,那么判断mark时添加等号,防止后侧遗留标签整体
        (!isEndPoint && (mark == -1 || mark > surfaceIndex))) {//如果不是结尾的点,那么判断mark时不加等号,判断前侧遗留标签整体
        return total;
    }
    //如果在原定位前,找到了'<',那么走一遍清除最前的富文本标签并计数的方法
    else {
        //清除一次标签,并将清除后的字符串保存
        newString = RemoveFrontRichTag(newString, surfaceIndex, ref count);
        //加入计数
        total += count;
        //回调,继续判断,原本定位前,是否有富文本标记
        TryGetRealIndex(newString, surfaceIndex, isEndPoint);
    }

    //递归完成后,返回最终的富文本标签字符总数
    return total;
}

最后创建GetRealIndex,用于获取最终索引,并返回

/// <summary>
/// 获取选定部分,排除富文本标签后(富文本标签也算作数量),字符的真实位置
/// </summary>
/// <param name="inputField"></param>
/// <param name="surfaceIndex"></param>
/// <param name="isEndPoint">
/// 默认为false,从选定位置往前,所有富文本标签<>全部排除(通常为选定部分的“前面”点)
/// 传入true后,从选定位置开始,不仅往前,而且往后,所有富文本标签<>全部排除(通常为选定部分“后面”点)
/// 注意:选定时,从前往后选和从后往前选,“前面”和“后面”的点要判断一下
/// 扩展:如果“前面”点传入true,而“后面”点传入false,那么可以获取选定区域开始,前后所有的富文本标签都会被选定(默认是清除前后所有富文本标签)
/// </param>
/// <returns></returns>
public int GetRealIndex(TMP_InputField inputField, int surfaceIndex,bool isEndPoint=false) {
    //
    string richText = inputField.text;

    //所有,surfaceIndex前,富文本标签所占的字符总数
    int allRichTagCharCount = 0;
    total = 0;//每次开始前,重置total为0

    //递归获取富文本标签所占的字符总数
    allRichTagCharCount = TryGetRealIndex(richText, surfaceIndex,isEndPoint);

    //最终实际的定位是,富文本字符总数与surfaceIndex表定位的合
    return allRichTagCharCount + surfaceIndex;
}

完整的脚本长这样

using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

public class RichTextTagHandler {

    private int total = 0;

    /// <summary>
    /// 获取选定部分,排除富文本标签后(富文本标签也算作数量),字符的真实位置
    /// </summary>
    /// <param name="inputField"></param>
    /// <param name="surfaceIndex"></param>
    /// <param name="isEndPoint">
    /// 默认为false,从选定位置往前,所有富文本标签<>全部排除(通常为选定部分的“前面”点)
    /// 传入true后,从选定位置开始,不仅往前,而且往后,所有富文本标签<>全部排除(通常为选定部分“后面”点)
    /// 注意:选定时,从前往后选和从后往前选,“前面”和“后面”的点要判断一下
    /// 扩展:如果“前面”点传入true,而“后面”点传入false,那么可以获取选定区域开始,前后所有的富文本标签都会被选定(默认是清除前后所有富文本标签)
    /// </param>
    /// <returns></returns>
    public int GetRealIndex(TMP_InputField inputField, int surfaceIndex,bool isEndPoint=false) {
        //
        string richText = inputField.text;

        //所有,surfaceIndex前,富文本标签所占的字符总数
        int allRichTagCharCount = 0;
        total = 0;//每次开始前,重置total为0

        //递归获取富文本标签所占的字符总数
        allRichTagCharCount = TryGetRealIndex(richText, surfaceIndex,isEndPoint);

        //最终实际的定位是,富文本字符总数与surfaceIndex表定位的合
        return allRichTagCharCount + surfaceIndex;
    }

    /// <summary>
    /// 尝试获取真实索引,递归方法,
    /// </summary>
    /// <param name="richText"></param>
    /// <param name="surfaceIndex"></param>
    /// <param name="isEndPoint"></param>
    /// <returns></returns>
    private int TryGetRealIndex(string richText, int surfaceIndex,bool isEndPoint) {

        string newString = richText;
        int count = 0;

        //先尝试定位‘<’,并获取它的定位
        int mark = newString.IndexOf('<');

        //-1是没找到,没找到或大于,原本的定位,那么代表,原本定位之前已经没有'<'了,直接返回结束

        if ((isEndPoint && (mark == -1 || mark >= surfaceIndex)) || //如果是结尾的点,那么判断mark时添加等号,防止后侧遗留标签整体
            (!isEndPoint && (mark == -1 || mark > surfaceIndex))) {//如果不是结尾的点,那么判断mark时不加等号,判断前侧遗留标签整体
            return total;
        }
        //如果在原定位前,找到了'<',那么走一遍清除最前的富文本标签并计数的方法
        else {
            //清除一次标签,并将清除后的字符串保存
            newString = RemoveFrontRichTag(newString, surfaceIndex, ref count);
            //加入计数
            total += count;
            //回调,继续判断,原本定位前,是否有富文本标记
            TryGetRealIndex(newString, surfaceIndex, isEndPoint);
        }

        //递归完成后,返回最终的富文本标签字符总数
        return total;
    }

    /// <summary>
    /// 从前往后,移除第一个富文本标签,计数标签字符数量,并将移除后的字符串返回
    /// </summary>
    /// <param name="aimString">待移除富文本标签的目标字符串</param>
    /// <param name="surfaceIndex">原定位</param>
    /// <param name="count">用来计数用的, 标记已经移除的富文本标签字符长度</param>
    /// <returns>阶段性返回当前移除的富文本标签字符数量,递归后最终返回所有符合要求的富文本标签字符总数量</returns>
    private string RemoveFrontRichTag(string aimString,int surfaceIndex, ref int count) {
        //先尝试定位'<'
        int meet = aimString.IndexOf('<');

        //如果定位不到,或者定位超过了原支付长度,说明标签在我们所选字符后面或者无富文本标签了,方法直接返回,并将数量count设置为0
        if (meet == -1 || meet > surfaceIndex) {
            count = 0;
            return aimString;
        }

        //成功定位到‘<’后,继续定位‘>’
        int leave = aimString.IndexOf('>');

        //将字符串拆分成去掉<>及其内部内容
        string newString = aimString.Substring(0, meet) + aimString.Substring(leave + 1, aimString.Length - leave - 1);

        //计算去掉部分的字符数量
        int length = leave - meet + 1;
        count += length;

        //Debug.Log(newString + " meet=" + meet + " leave=" + leave + " length=" + length);

        //把去掉已计数的标签后的字符串,返回
        return newString;
    }

    #region Utility

    //移除选定部分,所有富文本标签
    public string RemoveRichTextTags(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<.*?>", string.Empty);
    }

    //移除选定部分,所有颜色标签
    public string RemoveRichTextTags_color(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<color=.*?>|</color>", string.Empty);
    }

    //移除选定部分,所有加粗标签
    public string RemoveRichTextTags_B(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<b>|</b>", string.Empty);
    }

    //移除选定部分,所有Size标签
    public string RemoveRichTextTags_Size(string text) {
        return System.Text.RegularExpressions.Regex.Replace(text, "<size=.*?>|</size>", string.Empty);
    }

    #endregion
}

使用时,类似这样

private void Update() {
    if (Input.GetMouseButtonUp(0)) {
        OnMouseUpFromIpf();
    }

}

#region 主要代码

private void OnMouseUpFromIpf() {
    if (EventSystem.current.currentSelectedGameObject == ipf.gameObject) {

        int startPos = ipf.selectionAnchorPosition;//起始选择的位置
        int endPos = ipf.selectionFocusPosition;//结束时的位置

        if (startPos == endPos) return;//如果起始结束相等,那么相当于就点了一下,没选择内容

        string selectedStr = "";

        if (startPos < endPos) {//正常从前往后选的情况

            // 获取可见文本的起始位置,从选定Index开始,排除前面所有富文本标签
            currentInsideBeginIndex = handler.GetRealIndex(ipf, startPos);
            //获取可见文本的结束位置,从选定Index开始,后面的所有富文本标签,也要排除
            currentInsideEndIndex = handler.GetRealIndex(ipf, endPos, true);//此处传入true,即可以排除index后面的所有富文本标签
        }
        else if (startPos > endPos) {//从后往前选的情况

            currentInsideBeginIndex = handler.GetRealIndex(ipf, endPos);//反选,endPos是“前面的点”
            currentInsideEndIndex = handler.GetRealIndex(ipf, startPos, true);//反选,startPos是“后面的点”
        }

        //获取选定部分的字符串,排除选定区域开始,前后所有的富文本标签
        //注意:如果需要,以选定区域为基准,获取区域前后部分的所有富文本标签(而不是排除),那么需要将BeginIndex的GetRealIndex中IsEndPoint传入true,而EndIndex的GetRealIndex中IsEndPoint传入false
        //类似一个加粗区域,<b>A</b>,目前选定A,那么获取到的是“A”,若是以下面的写法反向设置IsEndPoint,那么获取到的是“<b>A</b>”。后面这么获取,可以方便对标签进行判断和修改
        selectedStr = ipf.text.Substring(currentInsideBeginIndex, currentInsideEndIndex - currentInsideBeginIndex);

        Debug.Log(currentInsideBeginIndex + " " + currentInsideEndIndex + " " + selectedStr+" "+startPos+" "+endPos);
        ipf_console.text = selectedStr;
    }
}

演示

其他扩展功能,只需要对字符串进行编辑即可

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

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

相关文章

前端 接口返回来的照片太大 加载慢如何解决

现象 解决 1. 添加图片懒加载 背景图懒加载 对背景图懒加载做的解释 和图片懒加载不同&#xff0c;背景图懒加载需要使用 v-lazy:background-image&#xff0c;值设置为背景图片的地址&#xff0c;需要注意的是必须声明容器高度。 <div v-for"img in imageList&quo…

麒麟 V10 离线 安装 k8s 和kuboard

目录 安装文件准备 主机准备 主机配置 修改主机名&#xff08;三个节点分别执行&#xff09; 配置hosts&#xff08;所有节点&#xff09; 关闭防火墙、selinux、swap、dnsmasq(所有节点) 安装依赖包&#xff08;所有节点&#xff09; 系统参数设置(所有节点) 时间同步…

html 引入vue Element ui 的方式

第一种&#xff1a;使用CDN的方式引入 <!--引入 element-ui 的样式&#xff0c;--> <link rel"stylesheet" href"https://unpkg.com/element-ui/lib/theme-chalk/index.css"> <!-- 必须先引入vue&#xff0c; 后使用element-ui --> <…

Pycharm通过配置隧道连接远程服务器

前言&#xff1a; 上篇有说到局域网windows和服务器互通的情况下连接远程pycharm&#xff0c;这次咱们来说下通过跳板机的方式连接服务器如何做到windows远程连接到服务器 1&#xff1a;设置SSH隧道或SSH代理 ssh -L localhost:LOCAL_PORT:FINAL_SERVER_IP:FINAL_SERVER_PORT…

【深度学习】深入探索卷积神经网络:从基础到先进架构”

卷积神经网络&#xff1a;深度学习的视觉之眼 在过去的十年中&#xff0c;深度学习已经彻底改变了我们处理和理解图像、视频及其他视觉媒体的方式。其中&#xff0c;卷积神经网络&#xff08;CNN&#xff09;无疑是这一革命的核心。本文将带您深入了解CNN的基础知识、关键发展…

CentOS 8服务器搭建L2TP服务器(over IPsec)操作指南

正文共&#xff1a;1234 字 14 图&#xff0c;预估阅读时间&#xff1a;2 分钟 之前发过把我自己的服务器搬上公网的文章&#xff08;我用100块钱把物理服务器放到了公网&#xff0c;省了几万块&#xff01;&#xff09;&#xff0c;当时L2TP拨号用的是网络上的解决方案&#x…

MySQL 修改数据

目录 数据插入-insert 不指定列名插入&#xff1a; 插入整行数据 格式&#xff1a; 多行数据插入 格式&#xff1a; 指定列名插入 插入1行 插入多行 更新字段-update 语法&#xff1a; 删除表 语法&#xff1a; 案例&#xff1a; 数据插入-insert INSERT 将数据行…

c++命令行解析开源库cxxopts上手教程

文章目录 cxxopts快速入门1. cmake环境配置2. 定义解析的规则3. 使用例子 cxxopts 简介 cxxopts是一个轻量级的C命令行解析库&#xff0c;它提供了易于使用的API来定义和解析命令行选项。它支持多种类型的选项&#xff0c;并且允许用户自定义选项的处理逻辑。 项目地址&#x…

Unity中支持泰语--没有版权限制

在Unity中支持泰语主要涉及以下几个方面&#xff1a; 选择合适的字体&#xff1a;在Unity中&#xff0c;确保使用支持泰文字符的字体是至关重要的。例如&#xff0c;可以选择使用Noto Serif Thai字体&#xff0c;这是一个支持泰语的字体2。 处理Unity版本问题&#xff1a;某些…

【C++之queue的应用及模拟实现】

C学习笔记---014 C之queue的应用及模拟实现1、queue的简单介绍2、queue的简单接口应用3、queue的模拟实现3.1、queue的结构一般的构建3.2、queue的适配器模式构建3.3、queue的主要接口函数 4、queue的模拟实现完整代码4.1、一般方式4.2、泛型模式 5、queue巩固练习题5.1、最小栈…

软件开发安全备受重视,浙江某运营商引入CWASP认证课程,

​浙江省某大型运营商是一家实力雄厚、服务优质的通信运营商&#xff0c;致力于为全省用户提供优质、高效的通信服务。数字时代&#xff0c;该运营商顺应信息能量融合发展趋势&#xff0c;系统打造以5G、算力网络、能力中台为重点的新型信息基础设施&#xff0c;夯实产业转型升…

VUE_H5页面跳转第三方地图导航,兼容微信浏览器

当前项目是uniapp项目&#xff0c;若不是需要替换uni.showActionSheet选择api onMap(address , organName , longitude 0, latitude 0){var ua navigator.userAgent.toLowerCase();var isWeixin ua.indexOf(micromessenger) ! -1;if(isWeixin) {const mapUrl_tx "…

nacos服务器挂了之后springboot/springcloud服务会挂吗?不会挂(顺便深入源码分析nacos配置中心客户端核心功能实现)

文章目录 nacos挂了之后服务会挂吗&#xff1f;不会挂&#xff08;深入源码分析&#xff09;展开nacos客户端源码找本地缓存配置相关文件客户端内存缓存客户端健康状态获取配置的实现 nacos挂了之后服务会挂吗&#xff1f;不会挂&#xff08;深入源码分析&#xff09; 展开nac…

MATLAB 浮点数 转化为 定点数

a fi(v,s,w,f) 一个 fi 对象&#xff0c;其值为 v&#xff0c;符号性为 s&#xff0c;字长为 w&#xff0c;小数长度为 f。 AD9361 a fi(0.707,1,12,11)

GPT建模与预测实战

代码链接见文末 效果图&#xff1a; 1.数据样本生成方法 训练配置参数&#xff1a; --epochs 40 --batch_size 8 --device 0 --train_path data/train.pkl 其中train.pkl是处理后的文件 因此&#xff0c;我们首先需要执行preprocess.py进行预处理操作&#xff0c;配置参数…

一个开源跨平台嵌入式USB设备协议:TinyUSB

概述 TinyUSB 是一个用于嵌入式系统的开源跨平台 USB 主机/设备堆栈&#xff0c;设计为内存安全&#xff0c;无需动态分配&#xff0c;线程安全&#xff0c;所有中断事件都被推迟&#xff0c;然后在非 ISR 任务函数中处理。查看在线文档以获取更多详细信息。 源码链接&#xff…

【U8+】打开固定资产卡片提示:运行时错误‘91’,未设置对象变量或with block变量。

【问题描述】 用友U8软件&#xff0c;固定资产模中打开某张卡片后&#xff0c; 提示&#xff1a;运行时错误‘91’&#xff0c;未设置对象变量或with block变量。 Ps&#xff1a;但不是所有卡片打开的时候都会提示&#xff0c;有的正常。 【解决方法】 跟踪数据库后&#xff…

基于单片机的智能模拟路灯控制系统

摘 要: 随着电力资源的紧缺,以及光污染和雾霾天气的影响,更智能化的路灯设计对人们的日常生活意义重大。本文的智能路灯控制系统是基于单片机的控制器,通过介绍该系统相应的硬件设计和软件设计,实现定时开关和依具体情况是否需要来开关路灯和进行亮度调节,并且具有自检功能…

Ubuntu 20.04 设置开启 root 远程登录连接

Ubuntu默认不设置 root 帐户和密码 Ubuntu默认不设置 root 帐户和密码 Ubuntu默认不设置 root 帐户和密码 如有需要&#xff0c;可在设置中开启允许 root 用户登录。具体操作步骤如下&#xff1a; 操作步骤 1、首先使用普通用户登录 2、设置root密码 macw:~$ sudo passwd …

Redis入门到通关之Redis通用命令

文章目录 Redis数据结构介绍Redis 通用命令命令演示KEYSDELEXISTSEXPIRE RedisTemplate 中的通用命令 Redis数据结构介绍 Redis 是一个key-value的数据库&#xff0c;key一般是String类型&#xff0c;不过value的类型多种多样&#xff1a; 贴心小建议&#xff1a;命令不要死…