Android: 如何使用TextView.setText()方法给字符串的一部分添加颜色?

90
我想通过.setText("")方法更改TextView视图的文本内容,同时对文本的一部分进行着色(或使其加粗、斜体、透明等),而不影响其他部分。例如:
title.setText("Your big island <b>ADVENTURE!</b>";
我知道上面的代码是错误的,但它有助于说明我想要实现什么。我该怎么做呢?

可能是如何更改TextView的一部分颜色?的重复问题。 - live-love
如果TextView中有很长的文本,可以使用更高效的方法 - Mingfei
可能是在Android中设置TextView span的颜色的重复问题。 - Suragch
使用Spans的Kotlin解决方案 https://dev59.com/Qm865IYBdhLWcg3wA5yc#59510004 - Dmitrii Leonov
15个回答

210

使用spans

例子:

final SpannableStringBuilder sb = new SpannableStringBuilder("your text here");

// Span to set text color to some RGB value
final ForegroundColorSpan fcs = new ForegroundColorSpan(Color.rgb(158, 158, 158)); 

// Span to make text bold
final StyleSpan bss = new StyleSpan(android.graphics.Typeface.BOLD); 

// Set the text color for first 4 characters
sb.setSpan(fcs, 0, 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE); 

// make them also bold
sb.setSpan(bss, 0, 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE); 

yourTextView.setText(sb);

11
我不太喜欢Android提供的解决方案,因为它无法适应多种语言,我也不知道我的单词有多少个字符。目前我正在寻找一个解决方案,在这个方案中我可以有多个spans,并将它们全部附加到一个TextView上,然后它们会被组合在一起。 - philipp
1
应该把注释中的“span使文本加粗”改为“span改变文本颜色”吗? - Ryhan
9
如果您的问题是要知道您的单词中有多少个字符,请在您的String或StringBuilder等对象上使用length()方法。 - Ahmed Adel Ismail
1
如果TextView中有很长的文本,这里有一种更有效的方法 - Mingfei
我最初在阅读答案时错过了这一点,但是您的结束索引必须比字符串中的结束索引多1(即要跨越前四个字母,您必须将[start,end]设置为[0,4]而不是[0,3]) - tir38
显示剩余2条评论

47
title.setText(Html.fromHtml("Your big island <b>ADVENTURE!</b>")); 

2
这比标记答案更好地解决了原问题。 - BSnapZ
1
如果您有换行符 \n,它不会显示换行符。 - live-love
9
不,它不更好,没有涂色功能,问题中提到了但是使用的方法fromHTML()很慢。 - Viktor Yakunin
1
那么使用 <font color="#FFAADD>文本</font> 不起作用吗? - milosmns
@milosmns 是的,它不能在所有版本上运行,特别是旧版本。这不是最安全的解决方案。 - Mihae Kheel

26

我希望这能对你有所帮助(它适用于多语言)。

<string name="test_string" ><![CDATA[<font color="%1$s"><b>Test/b></font>]]> String</string>

在您的Java代码中,您可以这样做:

int color = context.getResources().getColor(android.R.color.holo_blue_light);
String string = context.getString(R.string.test_string, color);
textView.setText(Html.fromHtml(string));

这样,只有“Test”这部分会被着色(并加粗)。


3
最佳答案。这对于国际化字符串非常有效。还有一个优点,就是能够在您的字符串资源中间放置彩色文本。 - jt-gilkeson
更多的解释请参考这里使用HTML标记进行样式设置部分。 - Eli
CDATA是什么使它必要? - Sarah Multitasker

22

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

val title = SpannableStringBuilder()
        .append("Your big island ")
        .bold { append("ADVENTURE") } 

titleTextField.text = title
boldSpannableStringBuilder的扩展函数。您可以在此处查看文档here,了解您可以使用的操作列表。
另一个例子:
val ssb = SpannableStringBuilder()
            .color(green) { append("Green text ") }
            .append("Normal text ")
            .scale(0.5F) { append("Text at half size ") }
            .backgroundColor(green) { append("Background green") }

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

甚至可以嵌套使用,这样你就可以得到像嵌入式DSL一样的东西:

bold { underline { italic { append("Bold and underlined") } } }

要使其正常工作,您需要在应用程序模块级别的build.gradle中添加以下内容:

repositories {
    google()
}

dependencies {
    implementation 'androidx.core:core-ktx:0.3'
}

另外,无论我放置哪种颜色,它都显示相同的紫色。 val spannableStringBuilder = SpannableStringBuilder() spannableStringBuilder.bold { underline { color(R.color.test_color) { append(underlinedText) } } } - Abhishek Saxena

17

这里有一个例子,它会查找所有出现的单词(不区分大小写),并将它们标记为红色:

String notes = "aaa AAA xAaax abc aaA xxx";
SpannableStringBuilder sb = new SpannableStringBuilder(notes);
Pattern p = Pattern.compile("aaa", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(notes);
while (m.find()){
    //String word = m.group();
    //String word1 = notes.substring(m.start(), m.end());

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

我喜欢你的名字 :P 我可以使用 new BackgroundColorSpan(Color.parseColor("#BFFFC6")) 更改特定单词的背景吗? - Ajay Pandya

9
你可以使用Spannable来给文本的某些部分设置特定的属性。如果你需要,我可以查找一个例子。
噢,就在这里的stackoverflow上:https://dev59.com/0nA75IYBdhLWcg3wdIz7
TextView TV = (TextView)findViewById(R.id.mytextview01); 
Spannable WordtoSpan = new SpannableString("I know just how to whisper, And I know just how to cry,I know just where to find the answers");        
WordtoSpan.setSpan(new ForegroundColorSpan(Color.BLUE), 15, 30, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TV.setText(WordtoSpan);

我们如何配置多语言文本的起始和结束范围? - Mubarak
一个想法是识别需要突出显示的单词,并将其提取为单独的字符串资源。在翻译短语时,您需要考虑更新单独的单词,以便在设置跨度时依赖于字符串和字符串长度的indexOf来动态设置跨度的起始和结束索引。 - Ionut Negru

4
我喜欢使用SpannableStringBuilder,通过逐个附加不同的span,而不是通过计算字符串长度来调用setSpan函数。
例如:(Kotlin代码)
val amountSpannableString = SpannableString("₹$amount").apply {
  // text color
  setSpan(ForegroundColorSpan("#FD0025".parseColor()), 0, length, 0)
  // text size
  setSpan(AbsoluteSizeSpan(AMOUNT_SIZE_IN_SP.spToPx(context)), 0, length, 0)
  // font medium
  setSpan(TypefaceSpan(context.getString(R.string.font_roboto_medium)), 0, length, 0)
}

val spannable: Spannable = SpannableStringBuilder().apply {
  // append the different spans one by one
  // rather than calling setSpan by calculating the string lengths
  append(TEXT_BEFORE_AMOUNT)
  append(amountSpannableString)
  append(TEXT_AFTER_AMOUNT)
}

Result


4
public static void setColorForPath(Spannable spannable, String[] paths, int color) {
    for (int i = 0; i < paths.length; i++) {
        int indexOfPath = spannable.toString().indexOf(paths[i]);
        if (indexOfPath == -1) {
            continue;
        }
        spannable.setSpan(new ForegroundColorSpan(color), indexOfPath,
                indexOfPath + paths[i].length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
}

使用

Spannable spannable = new SpannableString("Your big island ADVENTURE");
Utils.setColorForPath(spannable, new String[] { "big", "ADVENTURE" }, Color.BLUE);

textView.setText(spannable);

enter image description here


4

            String str1 = "If I forget my promise to ";
            String penalty = "Eat breakfast every morning,";
            String str2 = " then I ";
            String promise = "lose my favorite toy";
           

            String strb = "<u><b><font color='#081137'>"+ penalty +",</font></b></u>";
            String strc = "<u><b><font color='#081137'>"+ promise + "</font></b></u>";
            String strd = str1 +strb+ str2 + strc;
           tv_notification.setText(Html.fromHtml(strd));

或者使用这段代码:

    SpannableStringBuilder builder = new SpannableStringBuilder();
            SpannableString text1 = new SpannableString(str1);
            text1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.silver)), 0, str1.length() - 1, 0);
            builder.append(text1);

            SpannableString text2 = new SpannableString(penalty);
            text2.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.midnight)), 0, penalty.length(), 0);
            text2.setSpan(new UnderlineSpan(), 0, penalty.length(), 0);
            builder.append(text2);

            SpannableString text3 = new SpannableString(str2);
            text3.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.silver)),0, str2.length(), 0);
            builder.append(text3);


            SpannableString text4 = new SpannableString(promise);
            text4.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.midnight)), 0, promise.length(), 0);
            text4.setSpan(new UnderlineSpan(),0, promise.length(), 0);
            builder.append(text4);

          tv_notification.setText(builder);


4

如果您想使用HTML,您需要使用TextView.setText(Html.fromHtml(String htmlString))

如果您经常需要这样做/重复这样的操作,您可以查看我编写的一个类 (SpannableBuilder),因为Html.fromHtml()不是很高效(它在内部使用了一个巨大的XML解析器)。该类在此博客文章中有描述。


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