如何使TextView中的特定文字加粗?

368

我不知道如何让TextView上的特定文本变成粗体。

就像这样

txtResult.setText(id+" "+name);

我希望输出结果像这样:

1111 neil

idname 是我从数据库中检索到的变量值。我想使 id 变为粗体,但只有 id 而不影响到 name,我不知道该怎么做。


1
可能是在TextView中是否可以有多个样式?的重复问题。 - Ciro Santilli OurBigBook.com
31个回答

462

只需在HTML中构建您的字符串并设置:

String sourceString = "<b>" + id + "</b> " + name; 
mytextview.setText(Html.fromHtml(sourceString));

15
由于API 24版本起,fromHtml(sourceString)方法已被弃用,您需要使用以下代码:Spanned durationSpanned; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { durationSpanned = Html.fromHtml(durationFormatted, Html.FROM_HTML_MODE_LEGACY); } else { durationSpanned = Html.fromHtml(durationFormatted); } 该代码的作用是将HTML格式的文本转换为Spanned对象。如果您的设备API版本大于或等于24(即Android N或更高版本),则使用FROM_HTML_MODE_LEGACY模式解析HTML。否则,使用默认模式解析HTML。 - Mladen Rakonjac
2
如果您正在本地化您的应用程序,这是唯一可行的选择。 - howettl
14
Html.fromHtml() 已被弃用。 - Someone Somewhere
5
你可以使用以下代码: text.text = HtmlCompat.fromHtml( ctx.getString(R.string.logout_confirm), HtmlCompat.FROM_HTML_MODE_COMPACT ) 该代码用于将字符串资源文件中的文本进行HTML格式化,并将结果赋值给名为"text"的TextView控件。其中,ctx为上下文对象,R.string.logout_confirm是对应的字符串资源ID。 - Mahmoud Mabrok
@MahmoudMabrok 你的解决方案不再起作用了 - undefined
显示剩余7条评论

453

虽然你可以使用Html.fromHtml(),但你也可以使用一种更本地化的方法,即SpannableStringBuilder,这个帖子可能会有所帮助。

SpannableStringBuilder str = new SpannableStringBuilder("Your awesome text");
str.setSpan(new android.text.style.StyleSpan(android.graphics.Typeface.BOLD), INT_START, INT_END, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TextView tv=new TextView(context);
tv.setText(str);

如果这个答案不起作用,尝试在你的文本中添加可扩展字符串,类似于:mSpannable.append(yourremainingtext); - Gowtham Kumar
15
对于没有先前知识的从 Google 进来的人:INT_START/INT_END 是粗体应该开始和结束的位置。 - Spotlight
2
如果您只需要对源字符串应用一些标记而不更改它,则最好使用SpannableString类。 - Buckstabue
39
如果您需要本地化文本,这段代码就会完全失效。在不同的本地化环境中,INT_START 和 INT_END 的位置将会不同。 - howettl
6
如果有疑问,尝试理解代码的作用。int start 是您定义的数字,它是跨度起始索引。将 0 用作 start,将 string.().size() 用作 end。 - wtsang02
显示剩余3条评论

213

首先,您无需担心使用Raghav Sood的回答中的性能缓慢的代码

其次,在使用Kotlin时,您无需编写w3bshark的答案提供的扩展函数

最后,您只需要使用Google的Kotlin android-ktx库(请参见此处以查找更多信息并了解如何将其包含在您的项目中):

// Suppose id = 1111 and name = neil (just what you want). 
val s = SpannableStringBuilder()
          .bold { append(id) } 
          .append(name)
txtResult.setText(s) 

输出:1111 neil


更新:

因为我认为它可能对其他人有帮助,同时也可以展示您可以在这里做到什么程度,这里有更多的用例。

  • 当你需要显示一段文本,并且其中一些部分是蓝色和斜体时:

val myCustomizedString = SpannableStringBuilder()
    .color(blueColor, { append("A blue text ") })
    .append("showing that ")
    .italic{ append("it is painless") }
  • 当你需要同时显示粗体和斜体的文本时:

        bold { italic { append("Bold and italic") } }
    
  • 简而言之,boldappendcoloritalic是用于SpannableStringBuilder的扩展函数。您可以在官方文档中看到其他扩展函数,从中您可以想到其他可能性。


    5
    这很棒,但我认为与字符串有关的每个答案都应考虑翻译。在某些语言中,句子中的单词可以完全不同地排序和结构化,因此像这样附加并不总是适用于某些情况。而且,对于 XML 中的数量字符串来说也不是理想的解决方案。 - Edward van Raak
    2
    @CoolMind,这并不意味着您无法使用Android KTX。您可以在github.com/android/android-ktx上找到有关如何报告错误、提出功能建议或为该库做出贡献的信息。因此,它仍然是一个有效的参考。我已更新我的答案,包括有关该库以及如何在项目中使用它的更多信息。 - Filipe Brito
    20
    给这位提供现代解决方案和资源的人颁发一枚奖章!谢谢! - Sjd
    3
    男士们,你们让我感到非常开心。我花了几个小时看了这个,你们的解决方案非常优雅;-) - Alejandro Lagos
    3
    太棒了。 KTX使用的最佳案例之一。 - Zimin Byun
    显示剩余3条评论

    41

    我认为选定的答案没有提供令人满意的结果。我编写了自己的函数,它接受两个字符串;完整文本和要加粗的文本部分。

    它返回一个SpannableStringBuilder,其中将'text'中的'textToBold'加粗。

    我发现能够使子字符串加粗而不用将其包装在标签中很有用。

        /**
         * Makes a substring of a string bold.
         * @param text          Full text
         * @param textToBold    Text you want to make bold
         * @return              String with bold substring
         */
    
        public static SpannableStringBuilder makeSectionOfTextBold(String text, String textToBold){
    
            SpannableStringBuilder builder=new SpannableStringBuilder();
    
            if(textToBold.length() > 0 && !textToBold.trim().equals("")){
    
                //for counting start/end indexes
                String testText = text.toLowerCase(Locale.US);
                String testTextToBold = textToBold.toLowerCase(Locale.US);
                int startingIndex = testText.indexOf(testTextToBold);
                int endingIndex = startingIndex + testTextToBold.length();
                //for counting start/end indexes
    
                if(startingIndex < 0 || endingIndex <0){
                    return builder.append(text);
                }
                else if(startingIndex >= 0 && endingIndex >=0){
    
                    builder.append(text);
                    builder.setSpan(new StyleSpan(Typeface.BOLD), startingIndex, endingIndex, 0);
                }
            }else{
                return builder.append(text);
            }
    
            return builder;
      }
    

    你能提供一个如何在代码中使用它的例子吗? - Micro
    2
    @MicroR 你需要传递两个字符串,第二个字符串需要是包含在第一个字符串中的一段文本。例如:makeSectionOfTextBold("这是一些很棒的文本。", "很棒")。 - Leon
    1
    你应该将你的if语句改为if(textToBold != null && !textToBold.isEmpty())。 - Arda Kara
    3
    此函数不支持将子串在完整文本中多次出现。例如,在“My username is name”中,它无法将第二个“name”加粗。 - Jadamec
    1
    那么这个方法实际上应该被命名为makeAllBold(text, textToBold)。 - wtsang02

    40

    正如wtsang02所说,使用HTML会增加不必要的开销。建议使用原生解决方案。如果您不需要修改字符串,只需使用SpannableString,而不是SpannableStringBuilder。

    String boldText = "id";
    String normalText = "name";
    SpannableString str = new SpannableString(boldText + normalText);
    str.setSpan(new StyleSpan(Typeface.BOLD), 0, boldText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    textView.setText(str);
    

    36

    如果您想要使用来自XML的字符串,可以像这样操作:

    strings.xml(“CDATA”部分很重要,否则它将无法正常工作)


    strings.xml(“CDATA”部分很重要,否则它将无法正常工作)
    <string name="test">
         <![CDATA[
     <b>bold!</b> normal
        ]]>
    </string>
    

    布局文件

    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
            android:layout_gravity="center" />
    
    </FrameLayout>
    

    代码

    textView.text = HtmlCompat.fromHtml(getString(R.string.test), HtmlCompat.FROM_HTML_MODE_LEGACY)
    

    3
    这对我有用:<string name="test"><b>加粗文本</b>其他文本</string>。如果我写成 <![CDATA[ 则无效。 - sara
    @sara 现在再次测试,使用 Pixel 2 和 Q 版本。运行正常。我已经更新了 Kotlin 的工作方式,请再试一次。 - android developer
    如果你使用自定义字体,这就是可接受的答案。向创建者致敬。 - Raghul Sugathan
    @androiddeveloper FROM_HTML_MODE_LEGACY 这仍然是当前的做法吗? - David
    @Xam 那有什么区别吗? - android developer
    显示剩余2条评论

    25

    很简单,只需像这样关闭指定的文本,例如<b>"你的文本在这里:"</b>

    <string name="headquarters">"<b>"Headquarters:"</b>" Mooresville, North Carolina, U.S.</string>
    

    结果: 总部:美国北卡罗来纳州穆尔斯维尔。


    4
    只有在xml中使用字符串时,这才有效。如果以编程方式调用此字符串,则HTML编码会被删除。 - Starwave
    1
    这是最简单的答案,而且对我也有效。 - luckyhandler

    20

    如果您正在使用Kotlin,则使用 core-ktx 更易于执行此操作,因为它提供了一个针对特定领域的语言(DSL) 来实现此目的:

    val string: SpannedString = buildSpannedString {
        bold {
            append("foo")
        }
        append("bar")     
    }
    

    它提供的更多选项包括:

    append("Hello There")
    bold {
        append("bold")
        italic {
            append("bold and italic")
            underline {
                append("then some text with underline")
            }
        }
    }
    
    最后,您只需:
    textView.text = string
    

    14

    根据 @mladj0ni 的回答,我成功运行了下面的代码。问题是当你使用 String.format 时,它会剥离 html 标记,因此您必须在 strings.xml 中转义括号符号:

    strings.xml:

    <string name="welcome_messages">你好,%1$s!您有 &lt;b>%2$d 条新消息&lt;/b>。</string>

    code.java:

    String unspanned = String.format(Locale.US, "%s%s", getResources().getString(R.string. welcome_messages), 99);
    
    Spanned spanned;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
        spanned = Html.fromHtml(unspanned, Html.FROM_HTML_MODE_LEGACY);
    } else {
        spanned = Html.fromHtml(unspanned);
    }
    
    textView.setText(spanned);
    

    它比SpannableStringBuilder更简单。至于性能,如果您只显示一个字符串,那么用户不会注意到解析它需要额外的一毫秒。

    请查看此处的文档。


    感谢 <b>...</b> - oxied

    11

    如果您想将多个文本加粗,这里有一个更好的解决方案。我已经改进了Eitan的代码。感谢Eitan。

    public static SpannableStringBuilder makeSectionOfTextBold(String text, String... textToBold) {
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
    
        for (String textItem :
                textToBold) {
            if (textItem.length() > 0 && !textItem.trim().equals("")) {
                //for counting start/end indexes
                String testText = text.toLowerCase(Locale.US);
                String testTextToBold = textItem.toLowerCase(Locale.US);
                int startingIndex = testText.indexOf(testTextToBold);
                int endingIndex = startingIndex + testTextToBold.length();
    
                if (startingIndex >= 0 && endingIndex >= 0) {
                    builder.setSpan(new StyleSpan(Typeface.BOLD), startingIndex, endingIndex, 0);
                }
            }
        }
    
        return builder;
    }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接