TMP图文混排
- 方案一:TMP自带图文混排
- 使用方法
- 打包图集
- 使用
- 方案二:不打图集,可以使用任何图片
接到一个需求,TextMeshPro 图文混排。
方案一:TMP自带图文混排
优点 | 布局适应优秀,字体左中右布局位置都很不错 |
缺点 | 用到的图片需要打包,使用Resources加载 |
使用方法
打包图集
打包图集这里推荐使用TexturePacker
工具,功能强大,完美适配。这个软件也可以用于压缩图集。
TexturePacker链接
1.将你需要的散图或文件夹拖到这个区域,或使用添加精灵按钮添加。
2.选中下图按钮,选择Json(Array)
, 注意是Array。
下面的图片模式和打包可以自己尝试,主要是控制压缩和图片格式,这里不做介绍。
3.点击发布,获得一个Json文件和一张PNG,Json 是PNG各个图片的位置信息。
4.将上一步的资源导入Unity,然后打开TextMeshPro的图集工具 Sprite Importer。
5.将Json 和PNG 拖入下图中对应的位置。点击生成,然后存储到自己的文件夹
使用
1.将生成的图集拖入TMP的Sprite Asset中,使用图片时,在字符串中插入<sprite=你的图片id>,例如图中“<sprite=0> 图片好看吗”
2. ??? 图片咋这样??? ,发现图片大小不对,被勾选了自动转变为2的n次方。这里改成None ,图片能完整显示了
3. 还是不对,位置偏差好多,按下图2修改X和Y的偏移,最下方的Global Offset 是同时修改所有图片
完成啦,撒花~~
方案二:不打图集,可以使用任何图片
优点 | 当你有大量图片都可能用于图文混排,不方便打包时使用。代码没多少,随便修随便改- - |
缺点 | 对布局适配一般,只支持了整个组件的对齐,文字只支持了左对齐 |
原理就是将含图片的文本拆分成几段文本和图片的组合,计算位置,使用多个TMP和图片拼接
图片暂定格式 [img] 图片ID[/img]
using System.Collections.Generic;
using System.Text.RegularExpressions;
using TMPro;
using UnityEngine;
using UnityEngine.UI;public class TextImageView : MonoBehaviour
{public enum TextImageAlignmentH{Left,Center,Right}public enum TextImageAlignmentV{Top,Center,Bottom}public Transform content;public int FontSize;public float maxWidth;public TextMeshProUGUI tmp;public Image img;public TextImageAlignmentH alignmentH = TextImageAlignmentH.Center;public TextImageAlignmentV alignmentV = TextImageAlignmentV.Center;private float _lineHeight;private float _averageWidth;private float _maxLength;private List<KeyValuePair<string, int>> _contentList = new List<KeyValuePair<string, int>>();public string text{set { Test(value); }}public void Test(string text){_contentList = StringToKeyValuePair(text);var reduceWidth = 0f;var height = 0f;tmp.fontSize = FontSize;tmp.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, maxWidth);tmp.gameObject.SetActive(true);tmp.text = "test";tmp.ForceMeshUpdate();_lineHeight = tmp.textInfo.lineInfo[0].lineHeight;tmp.gameObject.SetActive(false);content.DestroyChildren();for (var i = 0; i < _contentList.Count; i++){if (_contentList[i].Value == 0){var label = TestText(_contentList[i].Key, reduceWidth,height);for (var i1 = 0; i1 < label.textInfo.lineInfo.Length; i1++){var lineInfo = label.textInfo.lineInfo[i1];var width = lineInfo.length;if(_maxLength < width){_maxLength = width;}if(i1 == label.textInfo.lineCount - 1){height = (label.transform.localPosition.y - i1*_lineHeight);reduceWidth = maxWidth - width - 5;}}}else{var imgData = TestImage(_contentList[i].Key, reduceWidth, height);reduceWidth = imgData.reduce + 5;if (_maxLength < (maxWidth - imgData.reduce)){_maxLength = maxWidth - imgData.reduce;}if(imgData.changeLine){height -= _lineHeight;}}}height -= _lineHeight;var x = 0f;switch (alignmentH){case TextImageAlignmentH.Left:x = 0;break;case TextImageAlignmentH.Center:x = (maxWidth - _maxLength) / 2;break;case TextImageAlignmentH.Right:x = maxWidth - _maxLength;break;}var y = 0f;switch (alignmentV){case TextImageAlignmentV.Top:y = _lineHeight;break;case TextImageAlignmentV.Center:y = -height/2 +_lineHeight/2;break;case TextImageAlignmentV.Bottom:y = -height;break;}content.transform.localPosition = new Vector3(x,y,0);}// <color=#FFFFFF><sprite=1/> 计分时\n<color=#FF0000>+4 </color>倍率</color>private List<KeyValuePair<string, int>> StringToKeyValuePair(string text){var result = new List<KeyValuePair<string, int>>();// 拆分字符串,匹配图片名和文字内容var splitText = Regex.Split(text, @"(\[img\]|\[/img\])");// 遍历拆分后的字符串for (var i = 0; i < splitText.Length; i++){switch (splitText[i]){case "[img]":{// 如果是图片名,将它加入结果列表result.Add(new KeyValuePair<string, int>(splitText[i + 1], 1));// 跳过处理下一个字符串i++;}continue;case "[/img]":// 如果是 [/img] ,跳过处理下一个字符串continue;default:{var content = splitText[i];if (string.IsNullOrEmpty(content)){continue;}result.Add(new KeyValuePair<string, int>(content, 0));}break;}}return result;}public string testValue;[ContextMenu("测试")]public void Test1(){// TestText(testValue, 300,0);Test(testValue);}private TextMeshProUGUI TestText(string value,float reduceWidth,float height){var obj = GameObject.Instantiate((UnityEngine.Object)tmp.gameObject) as GameObject;//GameObject obj = null;if (obj == null){return null;}obj.SetActive(true);if(_averageWidth == 0){_averageWidth = obj.GetComponent<TextMeshProUGUI>().GetPreferredValues(value).x/ value.Length;}var label = obj.GetComponent<TextMeshProUGUI>();label.fontSize = FontSize;label.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, maxWidth); obj.transform.SetParent(content);obj.transform.localScale = Vector3.one;if (reduceWidth <= _averageWidth)//剩下的空间不够一个字,直接换行{obj.transform.localPosition = new Vector3(0, height - _lineHeight, 0);}else{value = $"<space={maxWidth - reduceWidth}>{value}"; // 创建新字符串obj.transform.localPosition = new Vector3(0, height, 0); }value = DelUrlFlag(value);label.text = value;label.ForceMeshUpdate();return label; // 计算剩余宽度}private (bool changeLine,float reduce) TestImage(string value,float reduceWidth,float height){var obj = GameObject.Instantiate((UnityEngine.Object)img.gameObject) as GameObject;if (obj == null){return (false,reduceWidth);}obj.SetActive(true);var sp = obj.GetComponent<Image>();sp.SetIcon(int.Parse(value));//value是图片的ID,这里替换为自己的设置图片的方法var tempHeight = _lineHeight ;sp.SetNativeSize();var size = sp.rectTransform.sizeDelta;var rate = size.y / tempHeight;sp.rectTransform.sizeDelta = new Vector2(size.x / rate, tempHeight);var needWidth = sp.rectTransform.sizeDelta.x;obj.transform.SetParent(content);obj.transform.localScale = Vector3.one;if (needWidth > reduceWidth){obj.transform.localPosition = new Vector3(-maxWidth/2 +needWidth/2, height - _lineHeight, 0);return (true,maxWidth -needWidth);}else{obj.transform.localPosition = new Vector3(-maxWidth/2 + (maxWidth - reduceWidth)+needWidth/2, height, 0);return (false,reduceWidth - needWidth);}}private string DelUrlFlag(string strTxt){strTxt = strTxt.Replace("[/url]", "");int nStartLeftBracket = 0;int nStartRightBracket = 0;while (true){nStartLeftBracket = strTxt.IndexOf('[', nStartLeftBracket);nStartRightBracket = strTxt.IndexOf(']', nStartRightBracket);if (nStartLeftBracket > -1 && nStartRightBracket > -1 && nStartRightBracket > nStartLeftBracket){string sub = strTxt.Substring(nStartLeftBracket, nStartRightBracket - nStartLeftBracket + 1);if (sub.Contains("url")){strTxt = strTxt.Remove(nStartLeftBracket, nStartRightBracket - nStartLeftBracket + 1);nStartLeftBracket = 0;nStartRightBracket = 0;}else{nStartLeftBracket = nStartRightBracket;nStartRightBracket += 1;}}else{break;}}return strTxt;}
}