#region Copyright RenGuiYou. All rights reserved. //===================================================== // NeatlyFrameWork // Author: RenGuiyou // Feedback: mailto:750539605@qq.com //===================================================== #endregion using System; using System.Collections.Generic; using System.Text; using UnityEngine; using UnityEngine.UI; namespace Neatly.UI { public class NText : NMaskableGraphic, ILayoutElement { #region Proprety [SerializeField] private NFontData m_FontData = NFontData.defaultFontData; [TextArea(3, 10)] [SerializeField] protected string m_Text = string.Empty; [SerializeField] private bool m_SupportEmoji; [SerializeField] private bool m_MultiLanguageEnable; [SerializeField] private string m_LanguageKey; [SerializeField] private bool m_GradientEnable; [SerializeField] private bool m_SupportTab; [SerializeField] private Color m_GradientStartColor; [SerializeField] private Color m_GradientEndColor; private TextGenerator m_TextCache; private TextGenerator m_TextCacheForLayout; protected static Material s_DefaultText = null; [NonSerialized] protected bool m_DisableFontTextureRebuiltCallback; private bool m_WidthSettingInit; private TextGenerationSettings m_WidthSetting; private TextGenerationSettings WidthSetting { get { if (m_WidthSettingInit) return m_WidthSetting; m_WidthSetting = GetGenerationSettings(Vector2.zero); m_WidthSettingInit = true; return m_WidthSetting; } } private bool m_CurrentIsRTL; private string m_ResultText; #endregion protected override void Awake() { base.Awake(); if (Application.isPlaying) { SetI18nLabel(); } } protected NText() { useLegacyMeshGeneration = false; } public void SupportEmoji(bool isSupport = true) { m_SupportEmoji = isSupport; } public void SupportTab(bool isSupport = true) { m_SupportTab = isSupport; } public TextGenerator cachedTextGenerator { get { return m_TextCache ?? (m_TextCache = m_Text.Length != 0 ? new TextGenerator(m_Text.Length) : new TextGenerator()); } } public TextGenerator cachedTextGeneratorForLayout { get { return m_TextCacheForLayout ?? (m_TextCacheForLayout = new TextGenerator()); } } public override Texture mainTexture { get { if (font != null && font.material != null && font.material.mainTexture != null) return font.material.mainTexture; if (m_Material != null) return m_Material.mainTexture; return base.mainTexture; } } public void FontTextureChanged() { if (!this) { NFontUpdateTracker.UntrackText(this); return; } if (m_DisableFontTextureRebuiltCallback) return; cachedTextGenerator.Invalidate(); if (!IsActive()) return; if (CanvasUpdateRegistry.IsRebuildingGraphics() || CanvasUpdateRegistry.IsRebuildingLayout()) UpdateGeometry(); else SetAllDirty(); } public Font font { get { return m_FontData.font; } set { if (m_FontData.font == value) return; NFontUpdateTracker.UntrackText(this); m_FontData.font = value; NFontUpdateTracker.TrackText(this); #if UNITY_EDITOR // needed to track font changes from the inspector m_LastTrackedFont = value; #endif SetAllDirty(); } } public string text { get { return GetText(); } } public bool resizeTextForBestFit { get { return m_FontData.bestFit; } set { if (m_FontData.bestFit == value) return; m_FontData.bestFit = value; SetVerticesDirty(); SetLayoutDirty(); } } public int resizeTextMinSize { get { return m_FontData.minSize; } set { if (m_FontData.minSize == value) return; m_FontData.minSize = value; SetVerticesDirty(); SetLayoutDirty(); } } public int resizeTextMaxSize { get { return m_FontData.maxSize; } set { if (m_FontData.maxSize == value) return; m_FontData.maxSize = value; SetVerticesDirty(); SetLayoutDirty(); } } //锚点靠齐方式 public TextAnchor alignment { get { return m_FontData.alignment; } set { if (m_FontData.alignment == value) return; m_FontData.alignment = value; SetVerticesDirty(); SetLayoutDirty(); } } public int fontSize { get { return m_FontData.fontSize; } set { if (m_FontData.fontSize == value) return; m_FontData.fontSize = value; SetVerticesDirty(); SetLayoutDirty(); } } public HorizontalWrapMode horizontalOverflow { get { return m_FontData.horizontalOverflow; } set { if (m_FontData.horizontalOverflow == value) return; m_FontData.horizontalOverflow = value; SetVerticesDirty(); SetLayoutDirty(); } } public VerticalWrapMode verticalOverflow { get { return m_FontData.verticalOverflow; } set { if (m_FontData.verticalOverflow == value) return; m_FontData.verticalOverflow = value; SetVerticesDirty(); SetLayoutDirty(); } } public float lineSpacing { get { return m_FontData.lineSpacing; } set { if (m_FontData.lineSpacing == value) return; m_FontData.lineSpacing = value; SetVerticesDirty(); SetLayoutDirty(); } } public FontStyle fontStyle { get { return m_FontData.fontStyle; } set { if (m_FontData.fontStyle == value) return; m_FontData.fontStyle = value; SetVerticesDirty(); SetLayoutDirty(); } } public float pixelsPerUnit { get { var localCanvas = canvas; if (!localCanvas) return 1; if (!font || font.dynamic) return localCanvas.scaleFactor; if (m_FontData.fontSize <= 0 || font.fontSize <= 0) return 1; return font.fontSize / (float) m_FontData.fontSize; } } public void SetText(string value) { if (value == null) value = string.Empty; if (m_Text == value) return; m_Text = m_SupportEmoji ? ParseEmoji(value) : value; SetVerticesDirty(); SetLayoutDirty(); } public string GetText() { return m_Text; } public void SetI18nLabel() { if (m_MultiLanguageEnable && NeatlyUI.I18NLabel != null) { SetText(NeatlyUI.I18NLabel(m_LanguageKey)); } } protected override void OnEnable() { base.OnEnable(); cachedTextGenerator.Invalidate(); NFontUpdateTracker.TrackText(this); } protected override void OnDisable() { NFontUpdateTracker.UntrackText(this); base.OnDisable(); } protected override void UpdateGeometry() { if (font != null) { base.UpdateGeometry(); } } #if UNITY_EDITOR protected override void Reset() { AssignDefaultFont(); } private Font m_LastTrackedFont; public override void OnRebuildRequested() { NFontUpdateTracker.UntrackText(this); NFontUpdateTracker.TrackText(this); cachedTextGenerator.Invalidate(); base.OnRebuildRequested(); } protected override void OnValidate() { if (!IsActive()) { base.OnValidate(); return; } if (m_FontData.font != m_LastTrackedFont) { Font newFont = m_FontData.font; m_FontData.font = m_LastTrackedFont; NFontUpdateTracker.UntrackText(this); m_FontData.font = newFont; NFontUpdateTracker.TrackText(this); m_LastTrackedFont = newFont; } base.OnValidate(); } #endif internal void AssignDefaultFont() { font = Resources.GetBuiltinResource("Arial.ttf"); } public TextGenerationSettings GetGenerationSettings(Vector2 extents) { var settings = new TextGenerationSettings(); settings.generationExtents = extents; if (font != null && font.dynamic) { settings.fontSize = m_FontData.fontSize; settings.resizeTextMinSize = m_FontData.minSize; settings.resizeTextMaxSize = m_FontData.maxSize; } settings.textAnchor = m_FontData.alignment; settings.alignByGeometry = m_FontData.alignByGeometry; settings.scaleFactor = pixelsPerUnit; settings.color = color; settings.font = font; settings.pivot = rectTransform.pivot; settings.richText = m_SupportTab; settings.lineSpacing = m_FontData.lineSpacing; settings.fontStyle = m_FontData.fontStyle; settings.resizeTextForBestFit = m_FontData.bestFit; settings.updateBounds = false; settings.horizontalOverflow = m_FontData.horizontalOverflow; settings.verticalOverflow = m_FontData.verticalOverflow; return settings; } public static Vector2 GetTextAnchorPivot(TextAnchor anchor) { switch (anchor) { case TextAnchor.LowerLeft: return new Vector2(0, 0); case TextAnchor.LowerCenter: return new Vector2(0.5f, 0); case TextAnchor.LowerRight: return new Vector2(1, 0); case TextAnchor.MiddleLeft: return new Vector2(0, 0.5f); case TextAnchor.MiddleCenter: return new Vector2(0.5f, 0.5f); case TextAnchor.MiddleRight: return new Vector2(1, 0.5f); case TextAnchor.UpperLeft: return new Vector2(0, 1); case TextAnchor.UpperCenter: return new Vector2(0.5f, 1); case TextAnchor.UpperRight: return new Vector2(1, 1); default: return Vector2.zero; } } readonly UIVertex[] m_TempVerts = new UIVertex[4]; protected override void OnPopulateMesh(VertexHelper toFill) { if (font == null) return; m_DisableFontTextureRebuiltCallback = true; //绘制网格 Vector2 extents = rectTransform.rect.size; var settings = GetGenerationSettings(extents); float unitsPerPixel = 1 / pixelsPerUnit; IList verts; if (m_SupportTab) { var list = Translate1(); m_ResultText = UIListPool.GetString(list); cachedTextGenerator.PopulateWithErrors(m_ResultText, settings, gameObject); verts = cachedTextGenerator.verts; //处理颜色标签 int start = 0; for (int i = 0; i < list.Count; i++) { var item = list[i]; Color c = item.color; var str = item.isNewLine ? "\n" : item.GetTabString(); #if UNITY_2018_4_32 || UNITY_2019_1_OR_NEWER if (str == "\n") { continue; } #endif int end = start + str.Length; for (int j = start * 4; j < end * 4; j++) { if (verts.Count > j) { var v = verts[j]; v.color = c; verts[j] = v; } } start = end; } //回收资源 UIListPool.Release(list); } else { m_ResultText = Translate(); cachedTextGenerator.PopulateWithErrors(m_ResultText, settings, gameObject); // Apply the offset to the vertices verts = cachedTextGenerator.verts; } #if UNITY_2018_4_32 || UNITY_2019_1_OR_NEWER int vertCount = verts.Count; #else int vertCount = verts.Count - 4; #endif if (vertCount <= 0) { toFill.Clear(); return; } Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel; roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset; toFill.Clear(); #if UNITY_EDITOR #if UNITY_2018_4_32 || UNITY_2019_1_OR_NEWER bool changeColor = (m_ResultText.Replace("\n","").Replace(" ","").Length) * 4 > vertCount; #else bool changeColor = (m_ResultText.Length) * 4 > vertCount; #endif Color32 cc = new Color32(0, 0, 255, 255); if (roundingOffset != Vector2.zero) { for (int i = 0; i < vertCount; ++i) { int tempVertsIndex = i & 3; m_TempVerts[tempVertsIndex] = verts[i]; m_TempVerts[tempVertsIndex].position *= unitsPerPixel; m_TempVerts[tempVertsIndex].position.x += roundingOffset.x; m_TempVerts[tempVertsIndex].position.y += roundingOffset.y; if (changeColor) { m_TempVerts[tempVertsIndex].color = cc; } if (tempVertsIndex == 3) toFill.AddUIVertexQuad(m_TempVerts); } } else { for (int i = 0; i < vertCount; ++i) { int tempVertsIndex = i & 3; m_TempVerts[tempVertsIndex] = verts[i]; m_TempVerts[tempVertsIndex].position *= unitsPerPixel; if (changeColor) { m_TempVerts[tempVertsIndex].color = cc; } if (tempVertsIndex == 3) toFill.AddUIVertexQuad(m_TempVerts); } } #else if (roundingOffset != Vector2.zero) { for (int i = 0; i < vertCount; ++i) { int tempVertsIndex = i & 3; m_TempVerts[tempVertsIndex] = verts[i]; m_TempVerts[tempVertsIndex].position *= unitsPerPixel; m_TempVerts[tempVertsIndex].position.x += roundingOffset.x; m_TempVerts[tempVertsIndex].position.y += roundingOffset.y; if (tempVertsIndex == 3) toFill.AddUIVertexQuad(m_TempVerts); } } else { for (int i = 0; i < vertCount; ++i) { int tempVertsIndex = i & 3; m_TempVerts[tempVertsIndex] = verts[i]; m_TempVerts[tempVertsIndex].position *= unitsPerPixel; if (tempVertsIndex == 3) toFill.AddUIVertexQuad(m_TempVerts); } } #endif if (m_EmojiTuples != null && m_EmojiTuples.Count > 0) { EmojiRebuilder.Instance.RegistBuild(this); } m_DisableFontTextureRebuiltCallback = false; if (m_GradientEnable) { List vertexList = new List(); toFill.GetUIVertexStream(vertexList); float bottomY = vertexList[0].position.y; float topY = bottomY; for (int i = 1; i < vertexList.Count; i++) { float y = vertexList[i].position.y; if (y > topY) { topY = y; } else if (y < bottomY) { bottomY = y; } } float uiElementHeight = topY - bottomY; UIVertex v = new UIVertex(); for (int i = 0; i < toFill.currentVertCount; i++) { toFill.PopulateUIVertex(ref v, i); v.color = Color.Lerp(m_GradientEndColor, m_GradientStartColor, (v.position.y - bottomY) / uiElementHeight); toFill.SetUIVertex(v, i); } } } #region UI 虚方法 public virtual void CalculateLayoutInputHorizontal() { } public virtual void CalculateLayoutInputVertical() { } public virtual float minWidth { get { return 0; } } public virtual float preferredWidth { get { return cachedTextGeneratorForLayout.GetPreferredWidth(GetPreferredText(), GetGenerationSettings(Vector2.zero)) / pixelsPerUnit + 1; } } public virtual float flexibleWidth { get { return -1; } } public virtual float minHeight { get { return 0; } } public virtual float preferredHeight { get { var settings = GetGenerationSettings(new Vector2(rectTransform.rect.size.x, 0.0f)); return cachedTextGeneratorForLayout.GetPreferredHeight(GetPreferredText(), settings) / pixelsPerUnit; } } public virtual float flexibleHeight { get { return -1; } } public virtual int layoutPriority { get { return 0; } } #endregion #region 绘制Text #region 未开启标签模式 private bool DrawTabMode(VertexHelper toFill) { //绘制网格 Vector2 extents = rectTransform.rect.size; var settings = GetGenerationSettings(extents); var list = Translate1(); m_ResultText = UIListPool.GetString(list); cachedTextGenerator.PopulateWithErrors(m_ResultText, settings, gameObject); // Apply the offset to the vertices IList verts = cachedTextGenerator.verts; float unitsPerPixel = 1 / pixelsPerUnit; //最后四个点是新行 int vertCount = verts.Count - 4; if (vertCount <= 0) { toFill.Clear(); return false; } //处理颜色标签 int start = 0; for (int i = 0; i < list.Count; i++) { var item = list[i]; Color c = item.color; var str = item.isNewLine ? "\n" : item.GetTabString(); int end = start + str.Length; for (int j = start * 4; j < end * 4; j++) { var v = verts[j]; v.color = c; verts[j] = v; } start = end; } //回收资源 UIListPool.Release(list); Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel; roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset; toFill.Clear(); if (roundingOffset != Vector2.zero) { for (int i = 0; i < vertCount; ++i) { int tempVertsIndex = i & 3; m_TempVerts[tempVertsIndex] = verts[i]; m_TempVerts[tempVertsIndex].position *= unitsPerPixel; m_TempVerts[tempVertsIndex].position.x += roundingOffset.x; m_TempVerts[tempVertsIndex].position.y += roundingOffset.y; if (tempVertsIndex == 3) toFill.AddUIVertexQuad(m_TempVerts); } } else { for (int i = 0; i < vertCount; ++i) { int tempVertsIndex = i & 3; m_TempVerts[tempVertsIndex] = verts[i]; m_TempVerts[tempVertsIndex].position *= unitsPerPixel; if (tempVertsIndex == 3) toFill.AddUIVertexQuad(m_TempVerts); } } return true; } private bool DrawNormal(VertexHelper toFill) { //绘制网格 Vector2 extents = rectTransform.rect.size; var settings = GetGenerationSettings(extents); m_ResultText = Translate(); cachedTextGenerator.PopulateWithErrors(m_ResultText, settings, gameObject); // Apply the offset to the vertices IList verts = cachedTextGenerator.verts; float unitsPerPixel = 1 / pixelsPerUnit; //最后四个点是新行 int vertCount = verts.Count - 4; if (vertCount <= 0) { toFill.Clear(); return false; } Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel; roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset; toFill.Clear(); if (roundingOffset != Vector2.zero) { for (int i = 0; i < vertCount; ++i) { int tempVertsIndex = i & 3; m_TempVerts[tempVertsIndex] = verts[i]; m_TempVerts[tempVertsIndex].position *= unitsPerPixel; m_TempVerts[tempVertsIndex].position.x += roundingOffset.x; m_TempVerts[tempVertsIndex].position.y += roundingOffset.y; if (tempVertsIndex == 3) toFill.AddUIVertexQuad(m_TempVerts); } } else { for (int i = 0; i < vertCount; ++i) { int tempVertsIndex = i & 3; m_TempVerts[tempVertsIndex] = verts[i]; m_TempVerts[tempVertsIndex].position *= unitsPerPixel; if (tempVertsIndex == 3) toFill.AddUIVertexQuad(m_TempVerts); } } return true; } //取自适应的文本 private string GetPreferredText() { m_CurrentIsRTL = ArabicFixer.CheckRTL(m_Text); if (m_SupportTab) { var list = FormatTab(m_Text); if (m_CurrentIsRTL) { ArabicFixer.QuickFix1(list); } return UIListPool.GetString(list); } else { return m_CurrentIsRTL ? ArabicFixer.QuickFix(m_Text) : m_Text; } } private string Translate() { m_CurrentIsRTL = ArabicFixer.CheckRTL(m_Text); return m_CurrentIsRTL ? FormatText(ArabicFixer.QuickFix(m_Text)) : m_Text; } //单词解析 private string FormatText(string label) { float EmptySpaceWidth = GetCharLength(" "); string[] sourceArray = label.Split(ConstChar.NEW_LINE); StringBuilder sb = UIStringBuilder.Create(); StringBuilder lineSb = UIStringBuilder.Create(); for (int i = 0; i < sourceArray.Length; i++) { int offset = 0; var paragraph = sourceArray[i]; float remainWidth = width; for (int index = paragraph.Length - 1; index >= 0; index--) { offset++; char ch = paragraph[index]; if (UIHelper.IsSpace(ch)) { if (offset == 1) continue; string word = paragraph.Substring(index + 1, offset - 1); float wordLength = GetCharLength(word); float fullWordLength = wordLength + EmptySpaceWidth; //剩余放不下 if (remainWidth - fullWordLength < 0) { //无空格放得下 if (remainWidth - wordLength >= 0) { lineSb.Insert(0, word); AddLine(sb, lineSb); remainWidth = width; } //大词 else if (width < wordLength) { //有剩余,先换行 if (remainWidth < width) { AddLine(sb, lineSb); } //处理单词太长问题 DelLongWord(sb, word); AddLine(sb, lineSb); remainWidth = width; } else if (width < fullWordLength) { //先换行再塞词 新的开始 AddLine(sb, lineSb); lineSb.Insert(0, word); remainWidth = width - wordLength; } else { //先换行再塞词 新的开始 AddLine(sb, lineSb); lineSb.Insert(0, word); lineSb.Insert(0, ConstChar.EMPTY_SPACE); remainWidth = width - fullWordLength; } } else { lineSb.Insert(0, word); lineSb.Insert(0, ConstChar.EMPTY_SPACE); remainWidth -= fullWordLength; } offset = 0; } } if (offset > 0) { //最后一词的处理 string word = paragraph.Substring(0, offset); float wordLength = GetCharLength(word); //剩余放不下 if (remainWidth - wordLength < 0) { //大词 if (width < wordLength) { //有剩余,先换行 if (remainWidth < width) { AddLine(sb, lineSb); } //处理单词太长问题 DelLongWord(sb, word); } else { //先换行再塞词 新的开始 AddLine(sb, lineSb); lineSb.Insert(0, word); } } else { lineSb.Insert(0, word); } } AddLine(sb, lineSb); } sb.Length = sb.Length - 1; return UIStringBuilder.GetStringAndRelease(sb); } //处理长单词 private void DelLongWord(StringBuilder sb, string str) { StringBuilder temp = UIStringBuilder.Create(); for (int i = str.Length - 1; i >= 0; i--) { if (GetCharLength(str[i] + temp.ToString()) > width) { sb.Append(temp).Append("\n"); temp.Length = 0; } temp.Insert(0, str[i]); } if (temp.Length > 0) { sb.Append(temp); } UIStringBuilder.Release(temp); } //新增一行 private void AddLine(StringBuilder sb, StringBuilder lineSb = null) { if (lineSb != null) { if (lineSb.Length > 0) { sb.Append(lineSb); lineSb.Length = 0; } } sb.Append(ConstChar.NEW_LINE); } #endregion #region 开启标签模式 private List Translate1() { var list = FormatTab(m_Text); m_CurrentIsRTL = ArabicFixer.CheckRTL(m_Text); if (m_CurrentIsRTL) { ArabicFixer.QuickFix1(list); return FormatText(list); } return list; } //解析标签 private List FormatTab(string text) { var sb = UIStringBuilder.Create(); var itemList = UIListPool.Create(); var curItem = new StringInfo(); curItem.color = color; var beforeItem = curItem; var length = text.Length; for (var i = 0; i < length; i++) { var isMatch = false; if (text[i] == '<') { var endIndex = text.IndexOf('>', i); //粗体、斜体 if (endIndex - i == 2) { if (text[i + 1] == ConstChar.CHAR_ITALIC) { isMatch = true; curItem.isItalic = true; i += 2; } else if (text[i + 1] == ConstChar.CHAR_BOLD) { isMatch = true; curItem.isBold = true; i += 2; } } //颜色符 else if (endIndex - i == 10) { if (text[i + 1] == ConstChar.CHAR_COLOR && text[i + 2] == '=' && text[i + 3] == '#') { isMatch = true; ColorUtility.TryParseHtmlString(text.Substring(i + 3, 7), out var c); curItem.color = c; i += 10; } } //结束符 else if (endIndex - i == 3) { if (text[i + 2] == ConstChar.CHAR_ITALIC) { isMatch = true; curItem.isItalic = false; i += 3; } else if (text[i + 2] == ConstChar.CHAR_BOLD) { isMatch = true; curItem.isBold = false; i += 3; } else if (text[i + 2] == ConstChar.CHAR_COLOR) { isMatch = true; curItem.color = color; i += 3; } } } if (isMatch) { if (sb.Length > 0) { itemList.Add(new StringInfo() { text = sb.ToString(), isItalic = beforeItem.isItalic, isBold = beforeItem.isBold, color = beforeItem.color }); sb.Clear(); } beforeItem = curItem; } else { //最后一个字符 if (i == length - 1) { sb.Append(text[i]); itemList.Add(new StringInfo() { text = sb.ToString(), isItalic = curItem.isItalic, isBold = curItem.isBold, color = curItem.color }); sb.Clear(); } //新增一行 else if (text[i] == ConstChar.NEW_LINE) { itemList.Add(new StringInfo() { text = sb.ToString(), isItalic = curItem.isItalic, isBold = curItem.isBold, color = curItem.color }); //添加换行符 itemList.Add(new StringInfo() {isNewLine = true}); sb.Clear(); } else { sb.Append(text[i]); } } } UIStringBuilder.Release(sb); return itemList; } //单词解析 private List FormatText(List sourceArray) { var spaceWidth = GetCharLength(" "); var dst = UIListPool.Create(); var lineDst = UIListPool.Create(); //每一个标签样式的字符串拼接 var tabSb = UIStringBuilder.Create(); //回车符号Index var startIndex = -1; //遍历所有数据 for (var i = 0; i < sourceArray.Count; i++) { var current = 0; if (sourceArray[i].isNewLine) { current = i - 1; } else if (i == sourceArray.Count - 1) { current = i; } else { continue; } //单行剩余宽度 var remainWidth = width; //遍历单行数据 for (var j = current; j > startIndex; j--) { //空数据 if (sourceArray[j].text == null || sourceArray[j].text.Length <= 0) { continue; } var offset = 0; var str = sourceArray[j].text; var info = sourceArray[j]; //遍历StringInfo的字符串 for (var k = str.Length - 1; k >= 0; k--) { offset++; var ch = str[k]; //当前单词是不是空格 或者 当前单词是空格并且是第一个字符 if (!UIHelper.IsSpace(ch) || offset == 1) { continue; } var word = str.Substring(k + 1, offset - 1); var wordLength = GetCharLength(word); var fullWordLength = wordLength + spaceWidth; //剩余放不下 if (remainWidth - fullWordLength < 0) { //无空格放得下 if (remainWidth - wordLength >= 0) { tabSb.Insert(0, word); AddLine(dst, lineDst, tabSb, info); remainWidth = width; } //大词 else if (width < wordLength) { //有剩余,先换行 if (remainWidth < width) { AddLine(dst, lineDst, tabSb, info); } //处理单词太长问题 DelLongWord(dst, word, info); AddLine(dst, lineDst, tabSb, info); remainWidth = width; } else if (width < fullWordLength) { //先换行再塞词 新的开始 AddLine(dst, lineDst, tabSb, info); tabSb.Insert(0, word); remainWidth = width - wordLength; } else { //先换行再塞词 新的开始 AddLine(dst, lineDst, tabSb, info); tabSb.Insert(0, word).Insert(0, ConstChar.EMPTY_SPACE); remainWidth = width - fullWordLength; } } else { tabSb.Insert(0, word).Insert(0, ConstChar.EMPTY_SPACE); remainWidth -= fullWordLength; } offset = 0; } //结尾没有空格的数据 if (offset > 0) { //最后一词的处理 string word = str.Substring(0, offset); float wordLength = GetCharLength(word); //剩余放不下 if (remainWidth - wordLength < 0) { //大词 if (width < wordLength) { //有剩余,先换行 if (remainWidth < width) { AddLine(dst, lineDst, tabSb, info); } //处理单词太长问题 DelLongWord(dst, word, info); } else { //先换行再塞词 新的开始 AddLine(dst, lineDst, tabSb, info); tabSb.Insert(0, word); remainWidth -= wordLength; } } else { tabSb.Insert(0, word); remainWidth -= wordLength; } } //将这个tab剩余数据导入dst if (tabSb.Length > 0) { info.text = tabSb.ToString(); //储存这行没有放完的数据 lineDst.Insert(0, info); tabSb.Clear(); } } startIndex = i; if (sourceArray[i].isNewLine) { if (lineDst.Count > 0) { dst.AddRange(lineDst); lineDst.Clear(); } dst.Add(new StringInfo() {isNewLine = true}); } else if (i == sourceArray.Count - 1) { if (lineDst.Count > 0) { dst.AddRange(lineDst); lineDst.Clear(); } } } UIListPool.Release(lineDst); UIListPool.Release(sourceArray); UIStringBuilder.Release(tabSb); return dst; } //新增一行 private void AddLine(List dst, List lineDst, StringBuilder tabSb, StringInfo style) { if (lineDst.Count > 0) { dst.AddRange(lineDst); lineDst.Clear(); } if (tabSb.Length > 0) { style.text = tabSb.ToString(); dst.Add(style); tabSb.Clear(); } dst.Add(new StringInfo() {isNewLine = true}); } //处理长单词 private void DelLongWord(IList sb, string str, StringInfo style) { var temp = UIStringBuilder.Create(); for (var i = str.Length - 1; i >= 0; i--) { if (GetCharLength(str[i] + temp.ToString()) > width) { temp.Append("\n"); style.text = temp.ToString(); sb.Add(style); temp.Length = 0; } temp.Insert(0, str[i]); } if (temp.Length > 0) { style.text = temp.ToString(); sb.Add(style); } UIStringBuilder.Release(temp); } #endregion private float width { get { return rectTransform.rect.width; } } private float GetCharLength(string ch) { return cachedTextGeneratorForLayout.GetPreferredWidth(ch, GetGenerationSettings(Vector2.zero)) / pixelsPerUnit; if (Application.isPlaying) { return cachedTextGeneratorForLayout.GetPreferredWidth(ch, GetGenerationSettings(Vector2.zero)) / pixelsPerUnit; } float w = 0; CharacterInfo characterInfo; font.RequestCharactersInTexture(ch, fontSize, m_FontData.fontStyle); for (int i = 0; i < ch.Length; i++) { font.GetCharacterInfo(ch[i], out characterInfo, fontSize); w += characterInfo.advance; } return w; } private static float m_SceenScale = -1; private float SceenScale { get { if (m_SceenScale < 0) { m_SceenScale = (canvas.transform as RectTransform).rect.width / Screen.width; } return m_SceenScale; } } #endregion #region Emoji Support private static Dictionary EmojiRects { get { return NeatlyUI.EmojiRects; } } private List m_EmojiImages; private List EmojiImages { get { return m_EmojiImages ?? (m_EmojiImages = new List()); } } private UIBehaviourPool m_EmojiPool; private UIBehaviourPool EmojiPool { get { return m_EmojiPool ?? (m_EmojiPool = new UIBehaviourPool()); } } private List m_EmojiTuples; private List EmojiTuples { get { return m_EmojiTuples ?? (m_EmojiTuples = new List()); } } private string ParseEmoji(string label) { if (EmojiRects == null) return label; EmojiTuplePool.Release(m_EmojiTuples); if (!UIHelper.IsEmojiString(label)) { ClearEmoji(); return label; } StringBuilder sb = UIStringBuilder.Create(); int index = 0; while (index < label.Length) { if (index < (label.Length - 3)) { var fourChar = label.Substring(index, 4); if (EmojiRects.ContainsKey(fourChar)) { sb.Append(ConstChar.EMOJI_SPACE); EmojiTuples.Add(EmojiTuplePool.Create(sb.Length - 1, fourChar)); index += 4; continue; } } if (index < (label.Length - 1)) { var doubleChar = label.Substring(index, 2); if (EmojiRects.ContainsKey(doubleChar)) { sb.Append(ConstChar.EMOJI_SPACE); EmojiTuples.Add(EmojiTuplePool.Create(sb.Length - 1, doubleChar)); index += 2; continue; } } var singleChar = label.Substring(index, 1); if (EmojiRects.ContainsKey(singleChar)) { sb.Append(ConstChar.EMOJI_SPACE); EmojiTuples.Add(EmojiTuplePool.Create(sb.Length - 1, singleChar)); index++; continue; } if (!UIHelper.IsEmojiCharacter(label[index])) { sb.Append(label[index]); } index++; } return UIStringBuilder.GetStringAndRelease(sb); } public void BuildEmoji() { if (m_EmojiTuples == null || m_EmojiTuples.Count == 0) return; SpawnEmoji(m_EmojiTuples.Count); if (m_CurrentIsRTL) { int index = 0; for (int i = m_ResultText.Length - 1; i >= 0; i--) { if (m_ResultText[i] == ConstChar.EMOJI_SPACE) { m_EmojiTuples[index].SetPos(i); index++; } } } TextGenerator textGen = cachedTextGenerator.verts.Count == 0 ? cachedTextGeneratorForLayout : cachedTextGenerator; for (int i = 0; i < m_EmojiTuples.Count; i++) { var tuple = m_EmojiTuples[i]; int vertsIndex = tuple.pos * 4; if (vertsIndex < textGen.verts.Count - 4) { Vector2 pos = new Vector2(textGen.verts[vertsIndex].position.x, textGen.verts[vertsIndex].position.y); pos = pos * SceenScale; m_EmojiImages[i].transform.localPosition = pos; } } } private void SpawnEmoji(int count) { if (count == 0) { ClearEmoji(); return; } while (EmojiImages.Count < count) { var emoji = EmojiPool.Create(transform); var rt = emoji.rectTransform; rt.sizeDelta = new Vector2(fontSize, fontSize); rt.anchorMin = new Vector2(0, 1); rt.anchorMax = new Vector2(0, 1); rt.pivot = new Vector2(0, 0.16f); rt.localScale = Vector3.one; EmojiImages.Add(emoji); } if (EmojiImages.Count > count) { EmojiPool.Release(EmojiImages, EmojiImages.Count - count); } for (int i = 0; i < count; i++) { m_EmojiImages[i].SetSpriteName(EmojiRects[EmojiTuples[i].emoji]); } } private void ClearEmoji() { if (m_EmojiPool == null) return; m_EmojiPool.Release(m_EmojiImages); } #endregion } }