SpannableStringBuilder 创建具有多种字体/文本大小等的字符串示例?

Posted

技术标签:

【中文标题】SpannableStringBuilder 创建具有多种字体/文本大小等的字符串示例?【英文标题】:SpannableStringBuilder to create String with multiple fonts/text sizes etc Example? 【发布时间】:2012-06-05 09:22:16 【问题描述】:

我需要创建一个放置在 TextView 中的字符串,它将显示如下所示的字符串:

第一部分不加粗粗体其余不加粗

所以我想知道如何使用SpannableStringBuilder 来做到这一点?

我可以使用三个 TextEdit 来完成此操作,但我想使用最佳解决方案。

【问题讨论】:

【参考方案1】:

使用 html 类在 TextView 中使用 HTML 代码:

Spanned styledText = Html.fromHtml("First Part Not Bold <b>BOLD</b> rest not bold");
textView.setText(styledText);

【讨论】:

请记住,这真的很慢,如果在滚动过程中完成,可能会导致您跳帧。【参考方案2】:
First Part Not Bold   BOLD  rest not bold

您可以按照@Rajesh 的建议或按此操作。

String normalBefore= "First Part Not Bold ";
String normalBOLD=  "BOLD ";
String normalAfter= "rest not bold";
String finalString= normalBefore+normalBOLD+normalAfter;
Spannable sb = new SpannableString( finalString );
sb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), finalString.indexOf(normalBOLD)+ normalBOLD.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //bold
sb.setSpan(new AbsoluteSizeSpan(intSize), finalString.indexOf(normalBOLD)+ normalBOLD.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);//resize size

在 TextView 中显示

textview.setText(sb,  TextView.BufferType.SPANNABLE);

【讨论】:

另外我如何设置字体大小以及说粗体?所以我指定它是粗体并说字体 20? 另外请将索引更新为 (finalString.indexOf(normalBOLD),finalString.indexOf(normalBOLD)+normalBOLD.lenth() ) 除了 CodeDroid 所说的之外,另一个问题是 indexOf 方法可以捕获重复的单词并将您的 end 放在您的 start 之前以获得 IndexOutOfBoundsException。所以最好把子串格式化,然后放在一起。 @hotveryspicy 我怀疑“finalString.indexOf(normalBOLD)”这部分 - 与外表相反 - 高效,因为字符串实习......不是吗? 注意:android:textAllCaps="true" 将破坏 SpannableString【参考方案3】:

接受的答案很好(我赞成),但它未能按照提交者的要求使用 SpannableStringBuilder。因为我有一个 Builder 最有意义的案例,这里是代码(如果这对其他人有帮助,还可以更改文本的颜色)。请注意,您还可以将初始字符串提供给 SpannableStringBuilder 构造函数,但我在此处将其设置为使用“附加”以明确您可以在所需的“粗体”文本之前附加很多内容,然后如图所示记录开始。我怀疑这也是比接受的答案更快的代码。

SpannableStringBuilder longDescription = new SpannableStringBuilder();
longDescription.append("First Part Not Bold ");
int start = longDescription.length();
longDescription.append("BOLD");
longDescription.setSpan(new ForegroundColorSpan(0xFFCC5500), start, longDescription.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
longDescription.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, longDescription.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
longDescription.append(" rest not bold");

【讨论】:

另外,通过在添加之前检索文本粗体部分的起始位置,您不必担心应该是粗体的单词会重复出现。【参考方案4】:

所以我知道这已经解决了,即使按照 SpannableStringBuilder 的要求,但如果您想更动态地构建字符串,我想我会提出这个问题。

// Stuff needed
TextView DataTextView = (TextView)rootView.findViewById(R.id.DataView);
String Fields[] = ...database column names as strings... "x","y";

String DataString = new String();   

int start,stop;     // Start and Stop of formatting

// Final Result
SpannableStringBuilder coloredString = new SpannableStringBuilder(); 

SpannableString temp;       // Small segment of colored string
for (int i =0; i < Fields.length; i++)

    if (database_result.containsKey(Fields[i]))  // Be sure a field exists in the ContentValues
    
            DataString = Fields[i]+": ";
        start = DataString.length();
        DataString = DataString+ +database_result.getAsInteger(Fields[i])+" ";
        stop= DataString.length();
        temp = new SpannableString(DataString);
        temp.setSpan(new ForegroundColorSpan(Color.WHITE),start, stop, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        coloredString.append(temp);
       

DataTextView.setText(coloredString);

database_result 是我从 SQL 查询返回的 Cursor 类型构造的 ContentValues 类型。我遇到的唯一问题是起初它只是第一个部分的 ColorSpaning。每次您想在循环中使用一个(或任何其他类型的跨度)时,您都需要声明一个新的 ForegroundColorSpan 。

【讨论】:

【参考方案5】:

此代码应将 html 粗体标记内的所有内容设置为粗体。它还会删除标签,因此只显示里面的内容。

        SpannableStringBuilder sb = new SpannableStringBuilder("this is <b>bold</b> and this is <b>bold too</b>  and this is <b>bold too, again</b>.");

        Pattern p = Pattern.compile("<b>.*?</b>", Pattern.CASE_INSENSITIVE);            
        boolean stop = false;
        while (!stop)
        
            Matcher m = p.matcher(sb.toString());
            if (m.find()) 
                sb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                sb.delete(m.end()-4, m.end());
                sb.delete(m.start(), m.start() + 3);
            
            else
                stop = true;
        

这段代码也可以适配其他html风格的标签,比如Superscript(sup标签)等

        SpannableStringBuilder sb = new SpannableStringBuilder("text has <sup>superscript</sup> tag");

        Pattern p = Pattern.compile("<sup>.*?</sup>", Pattern.CASE_INSENSITIVE);            
        boolean stop = false;
        while (!stop)
        
            Matcher m = p.matcher(sb.toString());
            if (m.find()) 
                sb.setSpan(new SuperscriptSpan(), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                sb.delete(m.end()-6, m.end());
                sb.delete(m.start(), m.start() + 5);
            
            else
                stop = true;
        

要设置颜色,只需使用 ForegroundColorSpan 和 setSpan。

sb.setSpan(new ForegroundColorSpan(Color.rgb(255, 0, 0)), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);

希望对你有帮助。

【讨论】:

完美答案,你拯救了我的一天!我已经用 val p = Pattern.compile("([^", Pattern.MULTILINE 或 Pattern.DOTALL) 替换了模式,因为如果你只有开始标签 all 使用你的模式文本以粗体显示。谢谢【参考方案6】:

从 API 21 SpannableStringBuilder 开始包含一个简单的方法来执行此操作。这是一个解决方案示例:

SpannableStringBuilder builder= new SpannableStringBuilder();
StyleSpan boldSpan = new StyleSpan(android.graphics.Typeface.BOLD);
builder.append("First Part Not Bold ")
              .append("BOLD ", boldSpan, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
              .append("rest not bold");

由于您很可能不支持 API 21,因此您只能从该方法复制代码:

public SpannableStringBuilder append(CharSequence text, Object what, int flags) 
        int start = length();
        append(text);
        setSpan(what, start, length(), flags);
        return this;

【讨论】:

【参考方案7】:

既然可以使用 SpannableBuilder,为什么还要使用 SpannableStringBuilder? (https://gist.github.com/qtyq/90f9b4894069a8b3676c)

SpannableString ss = SpannableBuilder.init("First Part Not Bold BOLD rest not bold")
                                     .makeBold("BOLD")
                                     .create()

【讨论】:

因为 spannablebuilder 构建一个跨度,而 spannablestringbuilder 用于构建具有多个不同跨度的字符序列【参考方案8】:

我们也可以使用SpannableStringBuilder 和TextAppearanceSpan 来实现这一点。按照下面的步骤来实现。

    styles.xml中创建样式。

<style name="BoldStyle">
   <!-- Can add other styling attributes -->
   <item name="android:textStyle">bold</item>
   ......
</style>
    使用下面的代码。
SpannableStringBuilder builder = new SpannableStringBuilder("First Part Not Bold BOLD rest not bold"); builder.setSpan(new TextAppearanceSpan(this, R.style.BoldStyle), 20, 24, 0); ((TextView)findViewById(R.id.tv7)).setText(builder);

就是这样。希望它会帮助某人。

【讨论】:

【参考方案9】:

如果您使用 Kotlin,您可以使用 android-ktx 库执行以下操作

val s = SpannableStringBuilder()
        .append("First Part Not Bold ")
        .bold  append("BOLD")  
        .append("Rest not bold")

boldSpannableStringBuilder 的扩展功能。您可以查看文档here 了解您可以使用的操作列表。

另一个例子:

val s = SpannableStringBuilder()
            .color(green,  append("Green text ") )
            .append("Normal text ")
            .scale(0.5,  append("Text at half size " )
            .backgroundColor(green,  append("Background green") )

其中green 是已解析的 RGB 颜色。

甚至可以嵌套 span,这样您最终会得到一个嵌入式 DSL:

bold  underline  italic  append("Bold and underlined")   

您需要在您的应用模块级别 build.gradle 中使用以下内容才能使其工作:

repositories 
    google()


dependencies 
    implementation "androidx.core:core-ktx:1.2.0"

【讨论】:

如果您可以使用 Kotlin,这绝对是最佳选择。 DSL 使它成为一个非常非常好的 API。 它如何与 LiveData 一起工作?即使一切都在编译并且看起来还不错,我似乎无法让它应用这些更改? 最好用 kotlin 当时,当 Kotlin 不在的时候,使用 Spannable API 是一场噩梦......【参考方案10】:

对于Xamarin.Android

SpannableStringBuilder TextoFormateado = new SpannableStringBuilder();
                TextoFormateado.Append("Not Bold");
                int start = TextoFormateado.Length();

                TextoFormateado.Append("Bold and Red");
                TextoFormateado.SetSpan(new ForegroundColorSpan(new Color(255, 0, 0, 149)), 
                    start, TextoFormateado.Length(), SpanTypes.ExclusiveExclusive);
                TextoFormateado.SetSpan(new StyleSpan(TypefaceStyle.Bold), 
                    start, TextoFormateado.Length(), SpanTypes.ExclusiveExclusive);

                TextoFormateado.Append("Not bold");

                
                TxtFinalText.TextFormatted = TextoFormateado;

【讨论】:

【参考方案11】:

您可以在 kotlin 中加粗和调整字符串的一部分

val s = SpannableStringBuilder()
    .append("First Part Not Bold And No Resize ")
    .bold  scale(1.5f,  append("Second Part By Bold And Resize " ))  
    .append("Third Part Not Bold And No Resize")

yourTextview.text = s

【讨论】:

以上是关于SpannableStringBuilder 创建具有多种字体/文本大小等的字符串示例?的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin - 为setSpans()声明start,end和flasg时,为SpannableStringBuilder创建自定义ext函数,不带重复参数

SpannableString与SpannableStringBuilder

SpannableString与SpannableStringBuilder使用

SpannableString与SpannableStringBuilder使用

在 TextView 中使用 SpannableStringBuilder 的段落间距

使用 SpannableStringBuilder 时,为啥我的文本没有以样式显示?