Unity技术咨询:检测UI未显示文本及文本溢出字符定位方法
嘿,刚好在Unity项目里折腾过UI文本溢出的相关问题,给你分享下我实际用到的解决方案,分两部分来说:
1. 判断UI文本是否存在未显示(溢出)的情况
这里分UGUI原生Text组件和TextMeshPro的TMP_Text组件两种场景:
针对UGUI Text组件
核心思路是计算文本实际需要的渲染宽度,再和容器的可用宽度对比(要扣除内边距)。可以用TextGenerator来精准计算:
public bool IsTextOverflow(Text text) { TextGenerator textGenerator = new TextGenerator(); // 获取当前文本组件的生成设置 TextGenerationSettings genSettings = text.GetGenerationSettings(text.rectTransform.rect.size); // 填充文本内容到生成器 textGenerator.Populate(text.text, genSettings); // 计算文本实际需要的宽度 float requiredWidth = textGenerator.GetPreferredWidth(text.text, genSettings); // 计算容器的可用宽度(扣除左右内边距) float availableWidth = text.rectTransform.rect.width - text.padding.left - text.padding.right; // 实际宽度超过可用宽度则判定为溢出 return requiredWidth > availableWidth; }
注意:如果你的文本组件开启了
horizontalOverflow = HorizontalWrapMode.Wrap,文本会自动换行,这时候溢出判断应该基于高度而非宽度,把代码里的宽度计算换成高度逻辑即可。
针对TextMeshPro TMP_Text组件
TMP提供了更便捷的内置属性,也可以手动计算验证:
public bool IsTMPTextOverflow(TMP_Text tmpText) { // 直接用TMP自带的溢出判断属性(最便捷) if (tmpText.isTextOverflowing) return true; // 手动计算验证的方式 float requiredWidth = tmpText.preferredWidth; float availableWidth = tmpText.rectTransform.rect.width - tmpText.margin.left - tmpText.margin.right; return requiredWidth > availableWidth; }
2. 溢出时获取最后显示/第一个隐藏的字符
这个需求需要找到文本刚好超出容器宽度的临界点,这里同样分两种组件场景,还提供了性能优化的方案:
针对UGUI Text组件
基础逐字符遍历法(适合短文本)
逐字符累加计算宽度,直到找到溢出的那个字符:
public (char LastVisible, char FirstHidden) GetTextOverflowBoundary(Text text) { // 先判断是否溢出,没溢出直接返回最后一个字符和空字符 if (!IsTextOverflow(text)) return (text.text.LastOrDefault(), '\0'); TextGenerator textGenerator = new TextGenerator(); TextGenerationSettings genSettings = text.GetGenerationSettings(text.rectTransform.rect.size); float availableWidth = text.rectTransform.rect.width - text.padding.left - text.padding.right; char lastVisible = '\0'; char firstHidden = '\0'; for (int i = 0; i < text.text.Length; i++) { string subText = text.text.Substring(0, i + 1); textGenerator.Populate(subText, genSettings); float currentWidth = textGenerator.GetPreferredWidth(subText, genSettings); if (currentWidth > availableWidth) { firstHidden = text.text[i]; if (i > 0) lastVisible = text.text[i - 1]; break; } lastVisible = text.text[i]; } return (lastVisible, firstHidden); }
二分查找优化法(适合长文本)
如果文本很长,逐字符遍历性能不够,可以用二分查找快速定位最大可见字符数:
// 先获取最大可见字符数 private int GetMaxVisibleCharCount(Text text) { TextGenerator textGenerator = new TextGenerator(); TextGenerationSettings genSettings = text.GetGenerationSettings(text.rectTransform.rect.size); float availableWidth = text.rectTransform.rect.width - text.padding.left - text.padding.right; int left = 0; int right = text.text.Length; int maxVisibleCount = 0; while (left <= right) { int mid = (left + right) / 2; if (mid == 0) { left = mid + 1; continue; } string subText = text.text.Substring(0, mid); textGenerator.Populate(subText, genSettings); float subWidth = textGenerator.GetPreferredWidth(subText, genSettings); if (subWidth <= availableWidth) { maxVisibleCount = mid; left = mid + 1; } else { right = mid - 1; } } return maxVisibleCount; } // 再通过最大可见数获取边界字符 public (char LastVisible, char FirstHidden) GetTextOverflowBoundaryOptimized(Text text) { if (!IsTextOverflow(text)) return (text.text.LastOrDefault(), '\0'); int maxCount = GetMaxVisibleCharCount(text); char lastVisible = maxCount > 0 ? text.text[maxCount - 1] : '\0'; char firstHidden = maxCount < text.text.Length ? text.text[maxCount] : '\0'; return (lastVisible, firstHidden); }
针对TextMeshPro TMP_Text组件
TMP的文本信息更透明,可以直接通过textInfo获取每个字符的位置和尺寸,效率更高:
public (char LastVisible, char FirstHidden) GetTMPOverflowBoundary(TMP_Text tmpText) { if (!tmpText.isTextOverflowing) return (tmpText.text.LastOrDefault(), '\0'); // 强制更新网格信息,确保字符数据是最新的 tmpText.ForceMeshUpdate(); TMP_TextInfo textInfo = tmpText.textInfo; float availableWidth = tmpText.rectTransform.rect.width - tmpText.margin.left - tmpText.margin.right; char lastVisible = '\0'; char firstHidden = '\0'; for (int i = 0; i < textInfo.characterCount; i++) { TMP_CharacterInfo charInfo = textInfo.characterInfo[i]; // 字符的右边界超过容器可用宽度,判定为第一个隐藏字符 if (charInfo.xAdvance + charInfo.origin > availableWidth) { firstHidden = tmpText.text[i]; if (i > 0) lastVisible = tmpText.text[i - 1]; break; } lastVisible = tmpText.text[i]; } return (lastVisible, firstHidden); }
内容的提问来源于stack exchange,提问作者MBS
相关产品推荐
相关产品推荐

