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

 

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

 

前言

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

原理

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

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

注意

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

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

使用方法

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

代码

using BehaviorDesigner.Runtime.Tasks.Unity.UnityGameObject;
using System;
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 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;
        }

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

        //获取文本框宽度
        float boundWidth = Alignment(TextComponent.transform);

        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>();        
        Debug.LogError(rectTransform.sizeDelta.x);
            return rectTransform.sizeDelta.x;

    }

    /// <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>
    /// 获取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

参考文献

参考文献

引流GitHub

posted @ 2023-03-17 01:49  忘不了你那腋下的味道  阅读(108)  评论(0编辑  收藏  举报  来源