参考链接:
http://www.cnblogs.com/leoin2012/p/7162099.html
0.图片标签和图片类
标签格式:<icon name=*** w=1 h=1 n=*** p=***/>
RichTextImageInfo.cs
1 using UnityEngine; 2 3 public class RichTextImageInfo 4 { 5 public string name; //名字(路径) 6 public Vector2 size; //宽高 7 public Vector2 position; //位置 8 public int startVertex; //起始顶点 9 public int vertexLength; //占据顶点数 10 public Color color; //颜色 11 12 //标签属性 13 public float widthScale = 1f;//宽度缩放 14 public float heightScale = 1f;//高度缩放 15 public string eventName;//事件名 16 public string eventParameter;//事件参数 17 18 public void SetValue(string key, string value) 19 { 20 switch (key) 21 { 22 case "w": 23 { 24 float.TryParse(value, out widthScale); 25 break; 26 } 27 case "h": 28 { 29 float.TryParse(value, out heightScale); 30 break; 31 } 32 case "n": 33 { 34 eventName = value; 35 break; 36 } 37 case "p": 38 { 39 eventParameter = value; 40 break; 41 } 42 default: 43 break; 44 } 45 } 46 }
1.用空格替换图片标签
a.选择空格符
换行空格:Space键输出的空格,Unicode编码为\u0020,空格前后的内容是允许自动换行的。
不换行空格:Unicode编码为\u00A0,空格前后的内容是不允许自动换行的。
这里看一下两者的效果,如下图。上面的使用普通空格,而下面的使用不换行空格。
1 using UnityEngine.UI; 2 using UnityEngine; 3 4 [RequireComponent(typeof(Text))] 5 public class NonBreakingSpaceTextComponent : MonoBehaviour { 6 7 public static readonly string no_breaking_space = "\u00A0"; 8 protected Text text; 9 10 void Awake() 11 { 12 text = this.GetComponent<Text>(); 13 text.RegisterDirtyVerticesCallback(OnTextChange); 14 } 15 16 public void OnTextChange() 17 { 18 if (text.text.Contains(" ")) 19 { 20 text.text = text.text.Replace(" ", no_breaking_space); 21 } 22 } 23 }
显然这里选择不换行空格来进行替换。
b.计算图片所占空格数
首先要知道一个空格所占的宽度,图片的宽度,这样才能算出图片占几个空格,对应的,就是将标签替换为几个空格。
2.换行处理
当图片超过文本框边界时,在空格前插入换行符使图片换行。
3.图片位置计算
图片的位置为这些空格所占位置的中间
4.标签格式简化(待优化)
可以使用如{0},{1}这样的方式来简化图片路径标签,然后读取配置替换为真实的路径。
综上,可以得出如下的代码:
1 using System.Collections.Generic; 2 using System.Text.RegularExpressions; 3 using System.Text; 4 using UnityEngine.EventSystems; 5 using System; 6 using UnityEngine; 7 using UnityEngine.UI; 8 9 //图片<icon name=*** w=1 h=1 n=*** p=***/> 10 //下划线<material=underline c=#ffffff h=1 n=*** p=***>blablabla...</material> 11 public class RichText2 : Text { 12 13 private FontData fontData = FontData.defaultFontData; 14 15 //--------------------------------------------------------图片 start 16 private static readonly string replaceStr = "\u00A0"; 17 private static readonly Regex imageTagRegex = new Regex(@"<icon name=([^>\s]+)([^>]*)/>");//(名字)(属性) 18 private static readonly Regex imageParaRegex = new Regex(@"(\w+)=([^\s]+)");//(key)=(value) 19 private List<RichTextImageInfo> imageInfoList = new List<RichTextImageInfo>(); 20 private bool isImageDirty = false; 21 //--------------------------------------------------------图片 end 22 23 protected RichText2() 24 { 25 fontData = typeof(Text).GetField("m_FontData", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(this) as FontData; 26 } 27 28 readonly UIVertex[] m_TempVerts = new UIVertex[4]; 29 protected override void OnPopulateMesh(VertexHelper toFill) 30 { 31 if (font == null) 32 return; 33 34 // We don't care if we the font Texture changes while we are doing our Update. 35 // The end result of cachedTextGenerator will be valid for this instance. 36 // Otherwise we can get issues like Case 619238. 37 m_DisableFontTextureRebuiltCallback = true; 38 39 string richText = text; 40 IList<UIVertex> verts = null; 41 richText = CalculateLayoutWithImage(richText, out verts); 42 43 Vector2 extents = rectTransform.rect.size; 44 45 var settings = GetGenerationSettings(extents); 46 cachedTextGenerator.Populate(text, settings); 47 48 Rect inputRect = rectTransform.rect; 49 50 // get the text alignment anchor point for the text in local space 51 Vector2 textAnchorPivot = GetTextAnchorPivot(fontData.alignment); 52 Vector2 refPoint = Vector2.zero; 53 refPoint.x = Mathf.Lerp(inputRect.xMin, inputRect.xMax, textAnchorPivot.x); 54 refPoint.y = Mathf.Lerp(inputRect.yMin, inputRect.yMax, textAnchorPivot.y); 55 56 // Determine fraction of pixel to offset text mesh. 57 Vector2 roundingOffset = PixelAdjustPoint(refPoint) - refPoint; 58 59 // Apply the offset to the vertices 60 //IList<UIVertex> verts = cachedTextGenerator.verts; 61 float unitsPerPixel = 1 / pixelsPerUnit; 62 //Last 4 verts are always a new line... 63 int vertCount = verts.Count - 4; 64 65 toFill.Clear(); 66 if (roundingOffset != Vector2.zero) 67 { 68 for (int i = 0; i < vertCount; ++i) 69 { 70 int tempVertsIndex = i & 3; 71 m_TempVerts[tempVertsIndex] = verts[i]; 72 m_TempVerts[tempVertsIndex].position *= unitsPerPixel; 73 m_TempVerts[tempVertsIndex].position.x += roundingOffset.x; 74 m_TempVerts[tempVertsIndex].position.y += roundingOffset.y; 75 if (tempVertsIndex == 3) 76 toFill.AddUIVertexQuad(m_TempVerts); 77 } 78 } 79 else 80 { 81 //Debug.Log(unitsPerPixel); 82 for (int i = 0; i < vertCount; ++i) 83 { 84 int tempVertsIndex = i & 3; 85 m_TempVerts[tempVertsIndex] = verts[i]; 86 m_TempVerts[tempVertsIndex].position *= unitsPerPixel; 87 if (tempVertsIndex == 3) 88 toFill.AddUIVertexQuad(m_TempVerts); 89 //Debug.LogWarning(i + "_" + tempVertsIndex + "_" + m_TempVerts[tempVertsIndex].position); 90 } 91 } 92 m_DisableFontTextureRebuiltCallback = false; 93 } 94 95 protected string CalculateLayoutWithImage(string richText, out IList<UIVertex> verts) 96 { 97 Vector2 extents = rectTransform.rect.size; 98 var settings = GetGenerationSettings(extents); 99 100 float unitsPerPixel = 1 / pixelsPerUnit; 101 102 float spaceWidth = cachedTextGenerator.GetPreferredWidth(replaceStr, settings) * unitsPerPixel; 103 104 float fontSize2 = fontSize * 0.5f; 105 106 //解析图片标签,并将标签替换为空格 107 imageInfoList.Clear(); 108 Match match = null; 109 StringBuilder builder = new StringBuilder(); 110 while ((match = imageTagRegex.Match(richText)).Success) 111 { 112 RichTextImageInfo imageInfo = new RichTextImageInfo(); 113 imageInfo.name = match.Groups[1].Value; 114 string paras = match.Groups[2].Value; 115 if (!string.IsNullOrEmpty(paras)) 116 { 117 var keyValueCollection = imageParaRegex.Matches(paras); 118 for (int i = 0; i < keyValueCollection.Count; i++) 119 { 120 string key = keyValueCollection[i].Groups[1].Value; 121 string value = keyValueCollection[i].Groups[2].Value; 122 imageInfo.SetValue(key, value); 123 } 124 } 125 imageInfo.size = new Vector2(fontSize2 * imageInfo.widthScale, fontSize2 * imageInfo.heightScale); 126 imageInfo.startVertex = match.Index * 4; 127 int num = Mathf.CeilToInt(imageInfo.size.x / spaceWidth);//占据几个空格 128 imageInfo.vertexLength = num * 4; 129 imageInfoList.Add(imageInfo); 130 131 builder.Length = 0; 132 builder.Append(richText, 0, match.Index); 133 for (int i = 0; i < num; i++) 134 { 135 builder.Append(replaceStr); 136 } 137 builder.Append(richText, match.Index + match.Length, richText.Length - match.Index - match.Length); 138 richText = builder.ToString(); 139 } 140 141 // Populate charaters 142 cachedTextGenerator.Populate(richText, settings); 143 verts = cachedTextGenerator.verts; 144 // Last 4 verts are always a new line... 145 int vertCount = verts.Count - 4; 146 147 //换行处理 148 //0 1|4 5|8 9 149 //3 2|7 6|11 10 150 //例如前两个字为图片标签,第三字为普通文字;那么startVertex为0,vertexLength为8 151 for (int i = 0; i < imageInfoList.Count; i++) 152 { 153 RichTextImageInfo imageInfo = imageInfoList[i]; 154 int startVertex = imageInfo.startVertex; 155 int vertexLength = imageInfo.vertexLength; 156 int maxVertex = Mathf.Min(startVertex + vertexLength, vertCount); 157 //如果最边缘顶点超过了显示范围,则将图片移到下一行 158 //之后的图片信息中的起始顶点都往后移 159 if (verts[maxVertex - 2].position.x * unitsPerPixel > rectTransform.rect.xMax) 160 { 161 richText = richText.Insert(startVertex / 2, "\r\n"); 162 for (int j = i; j < imageInfoList.Count; j++) 163 { 164 imageInfoList[j].startVertex += 8; 165 } 166 cachedTextGenerator.Populate(richText, settings); 167 verts = cachedTextGenerator.verts; 168 vertCount = verts.Count - 4; 169 } 170 } 171 172 //计算位置 173 for (int i = imageInfoList.Count - 1; i >= 0; i--) 174 { 175 RichTextImageInfo imageInfo = imageInfoList[i]; 176 int startVertex = imageInfo.startVertex; 177 if (startVertex < vertCount) 178 { 179 UIVertex uiVertex = verts[startVertex]; 180 Vector2 pos = uiVertex.position; 181 pos *= unitsPerPixel; 182 pos += new Vector2(imageInfo.size.x * 0.5f, fontSize2 * 0.5f); 183 pos += new Vector2(rectTransform.sizeDelta.x * (rectTransform.pivot.x - 0.5f), rectTransform.sizeDelta.y * (rectTransform.pivot.y - 0.5f)); 184 imageInfo.position = pos; 185 imageInfo.color = Color.white; 186 } 187 else 188 { 189 imageInfoList.RemoveAt(i); 190 } 191 } 192 193 isImageDirty = true; 194 195 return richText; 196 } 197 198 protected void Update() 199 { 200 if (isImageDirty) 201 { 202 isImageDirty = false; 203 204 //回收当前的图片 205 Image[] images = GetComponentsInChildren<Image>(true); 206 for (int i = 0; i < images.Length; i++) 207 { 208 RichTextResourceManager.Instance.SetPoolObject(RichTextResourceType.Image, images[i].gameObject); 209 } 210 211 //生成图片 212 for (int i = 0; i < imageInfoList.Count; i++) 213 { 214 RichTextImageInfo imageInfo = imageInfoList[i]; 215 var name = imageInfo.name; 216 var position = imageInfo.position; 217 var size = imageInfo.size; 218 var color = imageInfo.color; 219 220 GameObject go = RichTextResourceManager.Instance.GetPoolObject(RichTextResourceType.Image); 221 Image image = go.GetComponent<Image>(); 222 RichTextResourceManager.Instance.SetSprite(name, image); 223 go.transform.SetParent(rectTransform); 224 go.transform.localScale = Vector3.one; 225 image.rectTransform.anchoredPosition = position; 226 image.rectTransform.sizeDelta = size; 227 image.color = color; 228 } 229 } 230 } 231 }
效果如下: