收到需求:一个文本输入框用来输入文字或者编辑文字,同时能够检测出是否输入了敏感字。
第一想法就是勾选Text的RichText,用标签的形式实现(<color=#ff0000>敏感字</color>),但是InputField会给出一个警告:Using Rich Text with input is unsupported(不支持);还有个问题,如果用标签,在删除时可能会破坏标签的完整性,如:出现"</color"的文本。
解决方案:重写Text的OnPopulateMesh(VertexHelper vh)函数,改变指定位置的顶点颜色(UIVertex)
原理和详细过程就不赘述了,参考文章:Unity Text关键词超链接/敏感词过滤算法 - 知乎 (zhihu.com)
参考文章中是增加下划线和增加点击效果,我这里稍微修改下,改变字体的颜色。
直接贴代码
//下面是keywordText脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public delegate void TextClickEvent(string str);
public delegate float CampareFunc(float a, float b);
struct Box
{
public float minX;
public float minY;
public float maxX;
public float maxY;
public Box(float x, float y, float X, float Y)
{
minX = x; minY = y; maxX = X; maxY = Y;
}
}
public class KeywordText : Text
{
private Dictionary<string, List<Box>> keywordBox = new Dictionary<string, List<Box>>();
protected override void OnPopulateMesh(VertexHelper toFill)
{
base.OnPopulateMesh(toFill);
if (text.Length == 0) return;
keywordBox.Clear();
List<int> lineStart = new List<int>();
List<int> indexMap = new List<int>();
string dueText = DueString(text, ref indexMap);
for (int i = 0; i < cachedTextGenerator.lineCount; i++)
{
if (cachedTextGenerator.lines[i].startCharIdx < text.Length)
lineStart.Add(cachedTextGenerator.lines[i].startCharIdx);
}
lineStart.Add(int.MaxValue);
List<UIVertex> vertexs = new List<UIVertex>(toFill.currentVertCount);
for (int i = 0; i < toFill.currentVertCount; i++)
{
UIVertex tempVertex = new UIVertex();
toFill.PopulateUIVertex(ref tempVertex, i);
vertexs.Add(tempVertex);
}
List<KeyValuePair<int, int>> keyWordLocation = Utils.GetInstance().MatchKeyWorld(dueText);
int nextLine = 1;
for (int i = 0; i < keyWordLocation.Count; i++)
{
int startChar = keyWordLocation[i].Key;
int endChar = keyWordLocation[i].Value;
int curStart = startChar;
while (lineStart[nextLine] <= indexMap[startChar]) nextLine++;
string curKeyword = dueText.Substring(startChar, endChar - startChar + 1);
for (int charIndex = startChar; charIndex <= endChar; charIndex++)
{
if (charIndex < endChar && indexMap[charIndex + 1] >= lineStart[nextLine])
{
SetClickBox(vertexs, curStart, charIndex, toFill, curKeyword);
curStart = charIndex + 1;
nextLine++;
}
}
SetClickBox(vertexs, curStart, endChar, toFill, curKeyword);
}
}
private void SetClickBox(List<UIVertex> vertexs, int startChar, int endChar, VertexHelper toFill, string keyword)
{
if (endChar < startChar || (endChar + 1) * 4 > vertexs.Count)
{
Debug.LogWarning("关键词超出rectTransform大小范围");
return;
}
//float minY = FindSuitableY(vertexs, startChar * 4, endChar * 4 + 3, (a, b) => a < b ? a : b);
//float maxY = FindSuitableY(vertexs, startChar * 4, endChar * 4 + 3, (a, b) => a > b ? a : b);
//float startX = vertexs[startChar * 4].position.x;
//float endX = vertexs[endChar * 4 + 2].position.x;
//Box curBox = new Box(startX, minY, endX, maxY);
//if (keywordBox.ContainsKey(keyword))
//{
// keywordBox[keyword].Add(curBox);
//}
//else
//{
// List<Box> BoxList = new List<Box>();
// BoxList.Add(curBox);
// keywordBox.Add(keyword, BoxList);
//}
//DrawUnderLine(toFill, new Vector2(startX, minY), new Vector2(endX, minY));
SetUVColor(vertexs, startChar * 4, endChar * 4 + 3, toFill);
}
private string DueString(string originalText, ref List<int> indexMap)
{
string resultStr = "";
indexMap.Clear();
for (int i = 0; i < originalText.Length; i++)
{
if (originalText[i] != ' ' && originalText[i] != '\n')
{
resultStr += originalText[i];
indexMap.Add(i);
}
}
return resultStr;
}
//private float FindSuitableY(List<UIVertex> vertexs, int startIndex, int endIndex, CampareFunc camp)
//{
// if (endIndex < startIndex || endIndex >= vertexs.Count)
// {
// Debug.LogError("FindSuitableY传入参数错误 startIndex = " + startIndex + " endIndex = " + endIndex + "顶点数量vertexs.count = " + vertexs.Count);
// return 0;
// }
// float needY = vertexs[startIndex].position.y;
// for (int i = startIndex + 1; i <= endIndex; i++)
// {
// needY = camp(needY, vertexs[i].position.y);
// }
// return needY;
//}
private UIVertex vertex = new UIVertex();
private void SetUVColor(List<UIVertex> vertexs, int startIndex, int endIndex, VertexHelper toFill)
{
if (endIndex < startIndex || endIndex >= vertexs.Count)
{
Debug.LogError("Test传入参数错误 startIndex = " + startIndex + " endIndex = " + endIndex + "顶点数量vertexs.count = " + vertexs.Count);
return;
}
for (int i = startIndex; i <= endIndex; i++)
{
//vertexs[i].color
toFill.PopulateUIVertex(ref vertex, i);
vertex.color = Color.red;
toFill.SetUIVertex(vertex, i);
}
}
}
修改部分
private void SetClickBox(List<UIVertex> vertexs, int startChar, int endChar, VertexHelper toFill, string keyword)
{
if (endChar < startChar || (endChar + 1) * 4 > vertexs.Count)
{
Debug.LogWarning("关键词超出rectTransform大小范围");
return;
}
//float minY = FindSuitableY(vertexs, startChar * 4, endChar * 4 + 3, (a, b) => a < b ? a : b);
//float maxY = FindSuitableY(vertexs, startChar * 4, endChar * 4 + 3, (a, b) => a > b ? a : b);
//float startX = vertexs[startChar * 4].position.x;
//float endX = vertexs[endChar * 4 + 2].position.x;
//Box curBox = new Box(startX, minY, endX, maxY);
//if (keywordBox.ContainsKey(keyword))
//{
// keywordBox[keyword].Add(curBox);
//}
//else
//{
// List<Box> BoxList = new List<Box>();
// BoxList.Add(curBox);
// keywordBox.Add(keyword, BoxList);
//}
//DrawUnderLine(toFill, new Vector2(startX, minY), new Vector2(endX, minY));
SetUVColor(vertexs, startChar * 4, endChar * 4 + 3, toFill);
}
//private float FindSuitableY(List<UIVertex> vertexs, int startIndex, int endIndex, CampareFunc camp)
//{
// if (endIndex < startIndex || endIndex >= vertexs.Count)
// {
// Debug.LogError("FindSuitableY传入参数错误 startIndex = " + startIndex + " endIndex = " + endIndex + "顶点数量vertexs.count = " + vertexs.Count);
// return 0;
// }
// float needY = vertexs[startIndex].position.y;
// for (int i = startIndex + 1; i <= endIndex; i++)
// {
// needY = camp(needY, vertexs[i].position.y);
// }
// return needY;
//}
private UIVertex vertex = new UIVertex();
private void SetUVColor(List<UIVertex> vertexs, int startIndex, int endIndex, VertexHelper toFill)
{
if (endIndex < startIndex || endIndex >= vertexs.Count)
{
Debug.LogError("Test传入参数错误 startIndex = " + startIndex + " endIndex = " + endIndex + "顶点数量vertexs.count = " + vertexs.Count);
return;
}
for (int i = startIndex; i <= endIndex; i++)
{
//vertexs[i].color
toFill.PopulateUIVertex(ref vertex, i);
vertex.color = Color.red;
toFill.SetUIVertex(vertex, i);
}
}