跳转至内容
钢蛋的工坊

Unity原生Text组件超出宽度自动补省略号

实现用一行文本显示玩家名字,如果玩家名字超出一个宽度,就自动截断并补充省略号。

Technology 1 分钟阅读

之前遇到一个需求,要用一行文本显示玩家名字,如果玩家名字超出一个宽度,就自动截断并补充省略号。

众所周知,Unity的TMP组件自带超出宽度自动补省略号的功能。然而玩家名字可以是任意语言的,一旦涉及多语言文本,就会出现一些字符显示不出来的问题,而用原生的Text能通过系统字体避免这个问题,但自动补省略号功能也一起没了。

看了网上的一些文章,大多数都在说可以使用TextGenerator类,里面有个TextGenerator.characterCountVisible属性,可以通过可见字符数和实际字符数对比,截取多余的字符并且补上省略号。然而理想和丰满,现实很骨感。尝试之后发现TextGenerator.characterCountVisible在即使真的没显示全的情况下,也和字符本来的长度一样,原因未知。

查阅Unity官方API文档,发现TextGenerator还有个GetPreferredWidth方法,于是有了个想法,是不是拿这个宽度和实际宽度对比,然后去除多余宽度内的字符就行了呢,不过这样就需要知道每个字符的宽度,TextGenerator有个GetCharactersArray方法,数组里面的UICharInfo类,有个charWidth字段,看起来就是这个宽度。试了一下,似乎成了。

超过宽度的文本自动补省略号 超过宽度的文本自动补省略号

未超过宽度的文本完整显示 未超过宽度的文本完整显示

截掉省略号前面多余的空格 截掉省略号前面多余的空格

附上源码,不过目前没有仔细测过,可以试试看~

private void SetTextTruncateWithEllipsis(Text txt, string value)
{
var rectTransform = txt.transform as RectTransform;
if (rectTransform == null)
{
return;
}
Vector2 selfSizeDelta = rectTransform.sizeDelta;
//拿到文本组件RectTransform的宽度
float selfWidth = selfSizeDelta.x;
//获取文本组件上序列化的配置
var settings = txt.GetGenerationSettings(selfSizeDelta);
var generator = new TextGenerator();
//计算填充文本需要的宽度
float preferredWidth = generator.GetPreferredWidth(value, settings);
//超出RectTransform部分的宽度
float overFlowWidth = preferredWidth - selfWidth;
if (overFlowWidth > 0)
{
generator.Populate(value, settings);
var allUiCharInfo = generator.GetCharactersArray();
int decreaseCharCount = 0;
for (int i = allUiCharInfo.Length - 2; i >= 0; i--)
{
if (overFlowWidth <= 0)
{
//如果删掉超过RectTransform部分的宽度的字符后,挨着省略号的文本是空格,那继续删字符
if (!char.IsWhiteSpace(value[i]))
{
break;
}
decreaseCharCount++;
}
if (overFlowWidth > 0)
{
//从后向前逐个删文本,直到不超过RectTransform部分的宽度,记录删掉的字符
overFlowWidth -= allUiCharInfo[i].charWidth;
decreaseCharCount++;
if (overFlowWidth <= 0)
{
//从不超格最后一个文本的前一个文本开始计算空格数
//因为最后一个文本肯定是要被替换成省略号的
i--;
}
}
}
//让文本在同一行显示
txt.horizontalOverflow = HorizontalWrapMode.Overflow;
txt.text = value.Substring(0, value.Length - decreaseCharCount - 1) + "...";
}
else
{
txt.text = value;
}
}