图文混排
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
public enum TagType
{
StaticImg = 1, //静态图片
DynamicImg = 2, //动态图片
}
public class TagData
{
public const float STATICIMG_SCALE = 2f;
public TagType Type;
public int spriteId;//UGUISpriteHolder的index
public int Length;
public string PopulateText;//填充文本
public Vector3 StartPosition;
public float Width;
public float Height;
public float imgProp = 1;//图片宽高比,图片大小以width为准,height用宽高比算出来
public int fontSize;
private int _startIndex;
private List<Vector4> _boundList;
public List<Vector4> boundList
{
get
{
return _boundList;
}
}
public TagData(string param, int fontSize,float imgProp)
{
this.Type = TagType.StaticImg;
this.fontSize = fontSize;
this.imgProp = imgProp;
switch (this.Type)
{
case TagType.StaticImg:
int.TryParse(param,out this.spriteId);
PopulateText = string.Format("<quad Size={0} Width={1}>", fontSize, STATICIMG_SCALE);
Width = fontSize * STATICIMG_SCALE;
Height = Width * imgProp;
break;
case TagType.DynamicImg:
int.TryParse(param, out this.spriteId);
PopulateText = string.Format("<quad Size={0} Width={1}>", fontSize, STATICIMG_SCALE);
Width = fontSize * STATICIMG_SCALE;
Height = Width * imgProp ;
break;
}
this.Length = PopulateText.Length;
}
public void SetStartIndex(int index)
{
_startIndex = index;
}
public int GetEndIndex()
{
return _startIndex + this.Length;
}
public void SetStartPosition(Vector3 position)
{
float offsetY = fontSize / 10;
position.Set(position.x, position.y - offsetY, position.z);
StartPosition = position;
}
}
public class ExBlendText : Text
{
private List<Sprite> spriteList;//要显示的图片
private Dictionary<int, TagData> tagDict;
private List<Image> showImgList = new List<Image>();//实例化出的eximage,Hide后丢入对象池
//private Dictionary<string, GameObject> showDynamicEmojiList = new Dictionary<string, GameObject>();//动态emoji,Hide后code + GameObject丢入对象池,通过code复用
//_dirty为true 重新创建tag内容
private bool _dirty = false;
private string _lastText = string.Empty;
private int _lastSize = -1;
private static readonly Regex _regex = new Regex(@"<tex=[0-9]>", RegexOptions.Singleline);
protected override void OnDestroy()
{
ClearAll(true);
}
public void InitBlendTextData(string text, UnityAction<bool, string, string> LongPressEvent, Canvas canvas = null, string colorString = "#F9B52C")
{
if (_lastText != text)
ClearAll();
_lastText = text;
_lastSize = fontSize;
_dirty = false;
string newText = Parse(text);
}
/// <summary>
/// 将特殊字符替换成富文本<tex=1>
/// </summary>
/// <param name="sourceString"></param>
/// <returns></returns>
private string Parse(string sourceString)
{
if (tagDict != null) tagDict.Clear();
if (sourceString == "") return "";
MatchCollection mc = _regex.Matches(sourceString);
StringBuilder sb = null;
int tagStartIndex = 0; //解析字符串后的tag的首个匹配下标
int lastMatchEndIndex = 0; //sourceString上次匹配成功的结束下标
int count = mc.Count;
for (int i = 0; i < count; i++)
{
Match match = mc[i];
float imgProp = 1;
if (spriteList.listSprite.Count > 0)
{
imgProp = (spriteList.listSprite[0].rect.height) / (spriteList.listSprite[0].rect.width);//图片的宽高比
}
TagData quadData = new TagData(match.Groups[1].Value, fontSize, imgProp);
if (sb == null) sb = new StringBuilder();
if (tagDict == null) tagDict = new Dictionary<int, TagData>();
string stringBeforeTag = sourceString.Substring(lastMatchEndIndex, match.Index - lastMatchEndIndex);
sb.Append(stringBeforeTag);
tagStartIndex += stringBeforeTag.Length;
quadData.SetStartIndex(tagStartIndex);
tagDict.Add(tagStartIndex, quadData);
sb.Append(quadData.PopulateText);
tagStartIndex += quadData.PopulateText.Length;
lastMatchEndIndex = match.Index + match.Length;
if (i == (mc.Count - 1))
{
if (lastMatchEndIndex < sourceString.Length)
{
sb.Append(sourceString.Substring(lastMatchEndIndex, sourceString.Length - lastMatchEndIndex));
}
}
}
if (sb != null)
{
return sb.ToString();
}
return sourceString;
}
protected new void Update()
{
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying)
{
return;
}
#endif
if (font == null)
return;
base.Update();
if (_lastSize != -1 && _lastSize != fontSize)
{
_lastSize = fontSize;
text = _lastText;
return;
}
if (_dirty)
{
_dirty = false;
TranslateTag();
}
}
/// <summary>
/// 将tag翻译成对应emoji等资源
/// </summary>
private void TranslateTag()
{
ClearAll();
if (tagDict != null)
{
foreach (TagData tagData in tagDict.Values)
{
switch (tagData.Type)
{
case TagType.StaticImg:
CreateStaticEmoji(tagData);
break;
case TagType.DynamicImg:
CreateDynamicEmoji(tagData);
break;
}
}
}
}
public void CreateStaticEmoji(TagData tagData)
{
Image exImage = GetImageFromPool();
exImage.gameObject.name = tagData.spriteId.ToString();
spriteList.SetImageSprite(tagData.spriteId ,exImage);
RectTransform rectTrans = exImage.GetComponent<RectTransform>();
rectTrans.SetParent(transform,false);
rectTrans.SetAsLastSibling();
rectTrans.sizeDelta = new Vector2(tagData.Width, tagData.Height);
rectTrans.anchoredPosition3D = tagData.StartPosition;
rectTrans.localScale = Vector3.one;
rectTrans.localRotation = Quaternion.identity;
rectTrans.pivot = Vector2.zero;
rectTrans.anchorMin = Vector2.zero;
rectTrans.anchorMax = Vector2.zero;
showImgList.Add(exImage);
}
public void CreateDynamicEmoji(TagData tagData)
{
//GameObject o = null;
//o = ChatController.PopDyEmoji(tagData.code);todo
//if (o == null)
//{
// //GameObject preObject = ResManager.GetObjectByPath("Pics/Emoji/" + tagData.code) as GameObject;
// //o = Instantiate(preObject, transform);
//}
//ExImage exImage = o.AddComponent<ExImage>();
//RectTransform rectTrans = o.GetComponent<RectTransform>();
//rectTrans.sizeDelta = new Vector2(tagData.Width, tagData.Height);
//rectTrans.anchoredPosition3D = tagData.StartPosition;
//rectTrans.localScale = Vector3.one;
//rectTrans.localRotation = Quaternion.identity;
//rectTrans.pivot = Vector2.zero;
//rectTrans.anchorMin = Vector2.zero;
//rectTrans.anchorMax = Vector2.zero;
//o.layer = 5;
//showDynamicEmojiList.Add(tagData.code, o);
}
//清除资源
public void ClearAll(bool destroy = false)
{
if (showImgList.Count != 0)
{
for (int i = 0; i < showImgList.Count; i++)
{
if(destroy)
{
Destroy(showImgList[i].gameObject);
}
else
{
showImgList[i].gameObject.SetActive(false);
}
}
}
//if (showDynamicEmojiList.Count != 0)
//{
// foreach (var emoji in showDynamicEmojiList.Values)
// {
// //TODO 对象池
// //ChatController.PushDyEmoji(emoji);
// Destroy(emoji.gameObject);
// }
// showDynamicEmojiList.Clear();
//}
}
/// <summary>
/// 看不见时隐藏,回收资源
/// </summary>
public void Hide()
{
gameObject.SetActive(false);
ClearAll();
}
public void Show()
{
gameObject.SetActive(true);
}
public Image GetImageFromPool()
{
Image img = null;
for(int i=0;i<showImgList.Count;i++)
{
if(!showImgList[i].gameObject.activeInHierarchy)
{
img = showImgList[i];
img.gameObject.SetActive(true);
break;
}
}
if(img == null)
{
GameObject o = new GameObject();
img = o.AddComponent<ExImage>();
}
return img;
}
readonly UIVertex[] m_TempVerts = new UIVertex[4];
protected override void OnPopulateMesh(VertexHelper toFill)
{
base.OnPopulateMesh(toFill);
if (font == null)
return;
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying)
{
return;
}
#endif
#region Text源码
m_DisableFontTextureRebuiltCallback = true;
Vector2 extents = rectTransform.rect.size;
var settings = GetGenerationSettings(extents);
cachedTextGenerator.PopulateWithErrors(text, settings, gameObject);
// Apply the offset to the vertices
IList<UIVertex> verts = cachedTextGenerator.verts;
float unitsPerPixel = 1 / pixelsPerUnit;
//Last 4 verts are always a new line... (\n)
int vertCount = verts.Count - 4;
Vector2 roundingOffset = new Vector2(verts[0].position.x, verts[0].position.y) * unitsPerPixel;
roundingOffset = PixelAdjustPoint(roundingOffset) - roundingOffset;
toFill.Clear();
Vector2 pivot = rectTransform.pivot;
float xOffset = rectTransform.rect.width * pivot.x;
float yOffset = rectTransform.rect.height * pivot.y;
Vector4 buttonBound = new Vector4(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue);
bool haveRoundingOffset = roundingOffset != Vector2.zero;
#endregion
for (int i = 0; i < vertCount; ++i)
{
int tempVertsIndex = i & 3;//text一个字符四个顶点
m_TempVerts[tempVertsIndex] = verts[i];
m_TempVerts[tempVertsIndex].position *= unitsPerPixel;
if (haveRoundingOffset)
{
m_TempVerts[tempVertsIndex].position.x += roundingOffset.x;
m_TempVerts[tempVertsIndex].position.y += roundingOffset.y;
}
if (tempVertsIndex == 3)//3,7,11,15,表示字符的左下顶点
{
int stringIndex = Mathf.FloorToInt(i / 4);
if (tagDict != null && tagDict.ContainsKey(stringIndex))
{
TagData tagData = tagDict[stringIndex];
float minX = float.MaxValue;
float minY = float.MaxValue;
for (int j = 0; j < 4; j++)
{
m_TempVerts[j].uv0 = Vector2.zero;//清除占位符显示 也可以用<color=#00000000><quad></color>来隐藏
if (m_TempVerts[j].position.x < minX) minX = m_TempVerts[j].position.x;//获取占位符左下角坐标
if (m_TempVerts[j].position.y < minY) minY = m_TempVerts[j].position.y;//获取占位符左下角坐标
}
//NDebug.LogError(minY + " " + yOffset);
tagData.SetStartPosition(new Vector3(minX + xOffset, minY + yOffset, 0));
}
toFill.AddUIVertexQuad(m_TempVerts);
}
}
m_DisableFontTextureRebuiltCallback = false;
_dirty = true;
}
}