【Unity】Text组件标点符号句首优化(Text标点符号开头、标点符号换行)

【Unity】Text组件标点符号句首优化

前言

今天碰到一个需求,项目中有时候的Text的文本会出现标点符号在句首的情况。
就像这样
需求是标点符号不能出现在句首,而且我们项目是自适应的,不同分辨率下Text的宽不同,这就导致了无论怎样修改文案,都可能会出现标点符号在句首的情况,所以要改进一下。
在网上搜到了一些解决方案的代码,放到项目里发现有问题没办法用,而且比较复杂有点难理解,所以我就研究了一下,写了一个比较简单的解决方案。

原理

暴力排序
首先我们要把字符串分割来看。
就像这样
那么通常情况下,Text应该是这样排列的
我排排排

相当于先获取到Text文本框的宽度,再获取到当前文本所占的宽(不同汉字的像素宽是不同的,所以没办法准确的获取一个汉字的宽)。
再进行判断,当放到某个字时文本的宽度超出了文本框的宽度,那么就说明这个字是要换行的。
再判断这个字是不是标点符号,是的话就把它和它前面的一个字拿到另一行。
就像这样
114514
那么这样就简单地实现了句首标点符号的处理方法

注意

没有加空格的判断,所以要确保文本中不能出现空格;
文本框必须能获取到宽度;
如果使用自动布局组件的话,偶尔情况下可能当前帧的文本框宽度为0,需要延迟处理

但是这个代码现在还有个缺陷,那就是经过处理的文本有时候会长短不一,看起来有点丑
就像这样
但是大部分情况下应该够用了

使用方法

通过Text组件调用
使用方法
不知道我的方法性能开销大不大,本人是Unity新手,如果有改进的地方欢迎提出来!!
其实应该还有另外的解决方案,比如TextMeshPro,感兴趣的小伙伴可以研究一下。

代码


using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 字符串标点符号格式化
/// </summary>
public static class StringPunctuationFormatting
{
    static List<string> punctuations = new List<string> {
        ",",    ",",
        "!",    "!",
        "。",    ".",
        "?",    "?",
        "~",
        "+",    "-",    "*",    "/",
        "《",    "<",
        "》",    ">",
        "、",    @"\",
        ":",    ":",
        ";",    ";",
        "“",    "\'",
        "”",    "‘",    "’",

    };

    static int MinFontSize = 30;
    static int MaxFontSize = 40;

    static IEnumerator FrameEnumerator(Text TextComponent)
    {
        yield return new WaitUntil(() => Alignment(TextComponent.transform) != 0);
        PunctuationFormat(TextComponent);

        //while (true)
        //{
        //    float result = Alignment(TextComponent.transform);
        //    if (result != 0)
        //    {
        //        PunctuationFormat(TextComponent);
        //        yield break;
        //    }
        //    yield return null;
        //}

    }

	///在对象或者父对象有自动布局组件的时候,会出现文本框宽度在下一帧才有具体值,因此要进行延迟处理
    /// <summary>
    /// 延迟执行标点符号处理
    /// </summary>
    public static void LateFramePunctuationFormat(this Text TextComponent)
    {
        TextComponent.StartCoroutine(FrameEnumerator(TextComponent));       
    }


    /// <summary>
    /// 标点符号格式化
    /// </summary>
    /// <param name="TextComponent">文本组件</param>
    public static void PunctuationFormat(this Text TextComponent)
    {
        //Debug.LogError("执行处理");  
        string text = TextComponent.text;

        if (text == string.Empty || text == "" || text.Length == 0)
        {
            Debug.LogError("字符串为空");
            return;
        }
		
        //获取文本框宽度
        float boundWidth = Alignment(TextComponent.transform);
        
        //如果Text组件使用bestfit,那么会根据文本框宽度计算字体大小,通常情况下SetTextFontSize方法需要根据项目需求改写
		if (TextComponent.resizeTextForBestFit)
		    TextComponent.fontSize = SetTextFontSize(text.Length, (int)boundWidth);

        

        TextGenerator generator = new TextGenerator();
        TextGenerationSettings settings = CopyFrom(TextComponent.GetGenerationSettings(TextComponent.rectTransform.rect.size));//获取文本框数据
        

        

        List<string> charList = StringFormat(text);
        List<string> stringList = new List<string>();
        string str = string.Empty;
        float width;
        foreach (var item in charList)
        {
            str += item;//当前组合的字符
            TextComponent.text = str;
            width = generator.GetPreferredWidth(TextComponent.text, settings) / settings.scaleFactor;//当前文本宽度
            if (width > boundWidth)//说明这次添加的字导致了换行
            {
                string line;//处理好的一行字
                if (isPunctuation(item))//判断是否为标点
                {
                    line = str.Substring(0, str.Length - 2);//将最后一个字拿给下一行
                    str = str.Substring(str.Length - 2, 2);
                }
                else
                {
                    line = str.Substring(0, str.Length - 1);
                    str = str.Substring(str.Length - 1, 1);
                }
                //再次判断最后一个字是否为标点
                lastOneIsPun(ref line, ref str);
                stringList.Add(line);
            }

            //如果是最后一次循环且没超行,
            //为了避免有和结尾一样的字时误以为结束,要判断内存地址是否相同
            if (object.ReferenceEquals(item, charList.LastOrDefault()))
            {
                stringList.Add(str);
            }
        }
        
        //以下处理单字不成行
        if (stringList.Count > 1 && stringList[stringList.Count - 1].Length <= 3) //判断最后一行字数,如果是单字的话
        {
            Debug.LogError("单子不成行");
            string newLine= stringList[stringList.Count - 2].Substring(0, stringList[stringList.Count - 2].Length - 1);
            string x= stringList[stringList.Count - 2].Substring(stringList[stringList.Count - 2].Length - 1, 1);//上一行的最后一个字
            stringList[stringList.Count - 2] = newLine;//删去一个字的新行
            stringList[stringList.Count - 1]=stringList[stringList.Count - 1].Insert(1, x);//添加给新一行
        }
        
        string endstring = string.Empty;
        for (int i = 0; i < stringList.Count; i++)
        {
            endstring += stringList[i];            

        }
        //foreach (var item in stringList)
        //{
        //    Debug.LogError(item);
        //    endstring += item;
        //}
        TextComponent.text = endstring;
    }
    /// <summary>
    /// 判断最后一个字是否为标点
    /// </summary>
    /// <returns></returns>
    static void lastOneIsPun(ref string line, ref string str)
    {
        if (isPunctuation(str[0].ToString()))//如果第一个字是标点
        {
            str = line.Substring(line.Length - 1, 1) + str;
            line = line.Substring(0, line.Length - 1);
            lastOneIsPun(ref line, ref str);
            return;
        }
        str = "\n" + str;
    }
    static bool isPunctuation(string item)
    {
        foreach (var pun in punctuations)
        {
            if (item == pun)
                return true;
        }
        return false;
    }

    /// <summary>
    /// 获取文本框的宽
    /// </summary>
    /// <param name="transform"></param>
    /// <returns>文本框宽度</returns>
    static float Alignment(Transform transform)
    {
        RectTransform rectTransform = transform.GetComponent<RectTransform>();
        return rectTransform.rect.width;
    }

    /// <summary>
    /// 格式化字符串
    /// </summary>
    /// <param name="str"></param>
    /// <returns>每个字符的列表</returns>
    static List<string> StringFormat(string str)
    {
        List<string> strlist = new List<string>();
        for (int i = 0; i < str.Length; i++)
        {
            strlist.Add(str.Substring(0, 1));
            str = str.Substring(1);
            i--;
        }
        return strlist;
    }


    /// <summary>
    /// 设置字体大小,最小为30最大为40。以文本框宽400,字体40为标准来计算:每多2个字字号减小1,文本框宽度每增加80,每多两个字的基础上再多一个字字号才减1
    /// </summary>
    /// <param name="length"></param>
    /// <returns></returns>
    static int SetTextFontSize(int length,int width)
    {
        int force = Mathf.Max(0, (width - 400) / 80);
        int newSize = MaxFontSize - (length - MaxFontSize) / (force+2);
        return Mathf.Clamp(newSize, MinFontSize, MaxFontSize);
    }


    /// <summary>
    /// 获取Text组件配置属性
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    static TextGenerationSettings CopyFrom(TextGenerationSettings o)
    {
        return new TextGenerationSettings
        {
            font = o.font,
            color = o.color,
            fontSize = o.fontSize,
            lineSpacing = o.lineSpacing,
            richText = o.richText,
            scaleFactor = o.scaleFactor,
            fontStyle = o.fontStyle,
            textAnchor = o.textAnchor,
            alignByGeometry = o.alignByGeometry,
            resizeTextForBestFit = o.resizeTextForBestFit,
            resizeTextMinSize = o.resizeTextMinSize,
            resizeTextMaxSize = o.resizeTextMaxSize,
            updateBounds = o.updateBounds,
            verticalOverflow = o.verticalOverflow,
            horizontalOverflow = o.horizontalOverflow,
            generationExtents = o.generationExtents,
            pivot = o.pivot,
            generateOutOfBounds = o.generateOutOfBounds
        };
    }

}



示例//未更新

示例Demo

参考文献

参考文献

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值