在TextView中突出显示文本

13

我有一个背景色随机的TextView(可以是任何颜色),上面有一些需要可读的文本。

我认为最好的解决方案是用白色突出显示所述文本,并将文本颜色设置为黑色。

我的问题是:是否可能从XML中突出显示TextView内部的文本?

在我的布局中,我有以下内容:

  <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/colorButton4"
        android:layout_gravity="right|bottom"
        android:background="@drawable/layout_border"
        android:layout_marginRight="30dp"
        android:layout_marginBottom ="30dp"
        android:clickable="true"
        android:onClick="onClick"
        android:gravity="center"
        android:textColorHighlight="@color/bgWhite"
        android:textColor="@color/Black"
        android:text="5431354" />

但它并没有突出显示文本。


你说的“highlight”是什么意思? - finki
2
无论TextView的背景颜色如何,都要使文本可见。 就像这样:http://i1.wp.com/texblog.org/Wordpress/wp-content/uploads/2015/05/latex-highlight-text.png - Slamit
请查看我的答案 https://dev59.com/O2w15IYBdhLWcg3wFHtZ#47142724 - Sumit
8个回答

18
你可能想要使用SpannableString来完成这个任务,它可以让TextView中的字符串的不同部分以不同的样式呈现。
就像这样:
    SpannableString str = new SpannableString("Highlighted. Not highlighted.");
    str.setSpan(new BackgroundColorSpan(Color.YELLOW), 0, 11, 0);
    textView.setText(str);

那么你是在告诉我,如果没有在我的Java代码中有它,就无法完成吗? - Slamit
可以使用一些特定的HTML标签在strings.xml中为文本添加样式。尝试使用以下其中一个答案:https://dev59.com/MWMm5IYBdhLWcg3whPPq,但是使用背景颜色而不是字体颜色。 - Egg
谢谢提供链接。它提供了一些有趣的观点!不幸的是,没有一个仅使用XML的解决方案可行。我想我必须使用一些Java代码来解决这个问题。 - Slamit
@Slamit 嗯,我想我还要写一些Java代码来实现高亮显示。 - Moshi
请点击以下链接:https://github.com/datanapps/HighlightedTextView - Yogendra

13

简单方法

您可以使用Spannable 类来格式化文本。

textView.setText("Hello, I am Awesome, Most Awesome"); // set text first
setHighLightedText(textView, "a"); // highlight all `a` in TextView

输出结果将类似于下面的图片。

Output

这是方法。

 /**
     * use this method to highlight a text in TextView
     *
     * @param tv              TextView or Edittext or Button (or derived from TextView)
     * @param textToHighlight Text to highlight
     */
    public void setHighLightedText(TextView tv, String textToHighlight) {
        String tvt = tv.getText().toString();
        int ofe = tvt.indexOf(textToHighlight, 0);
        Spannable wordToSpan = new SpannableString(tv.getText());
        for (int ofs = 0; ofs < tvt.length() && ofe != -1; ofs = ofe + 1) {
            ofe = tvt.indexOf(textToHighlight, ofs);
            if (ofe == -1)
                break;
            else {
                // set color here
                wordToSpan.setSpan(new BackgroundColorSpan(0xFFFFFF00), ofe, ofe + textToHighlight.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                tv.setText(wordToSpan, TextView.BufferType.SPANNABLE);
            }
        }
    }

您可以查看此答案以获取可点击的高亮文本。


谢谢你的建议,Khemraj。问题比较严格,因为我需要从XML中实现(而不是通过Java)。你提供的方法很遗憾无法帮助我!我认为这是不可能的,所以我重新设计了我的布局,采用了另一种解决方案! - Slamit
你应该给“ofe”取一个更好的名字。此外,你不需要在循环内调用“setText”。在循环外设置它就足够了。 - android developer
1
我该如何使用不区分大小写的方式进行匹配? - JerseyDevel
如何在不考虑大小写的情况下突出显示文本? - Amin
如果单词的首字母为大写字母,请改变第一行 使用 String tvt = tv.getText().toString().toLowerCase(); 而不是 String tvt = tv.getText().toString(); - J El
显示剩余2条评论

12

要突出显示所有特定文本的出现,请使用以下方法:

private void highlightString(String input) {
//Get the text from text view and create a spannable string
SpannableString spannableString = new SpannableString(mTextView.getText());
//Get the previous spans and remove them
BackgroundColorSpan[] backgroundSpans = spannableString.getSpans(0, spannableString.length(), BackgroundColorSpan.class);

for (BackgroundColorSpan span: backgroundSpans) {
    spannableString.removeSpan(span);
}

//Search for all occurrences of the keyword in the string
int indexOfKeyword = spannableString.toString().indexOf(input);

while (indexOfKeyword > 0) {
    //Create a background color span on the keyword
    spannableString.setSpan(new BackgroundColorSpan(Color.YELLOW), indexOfKeyword, indexOfKeyword + input.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

    //Get the next index of the keyword
    indexOfKeyword = spannableString.toString().indexOf(input, indexOfKeyword + input.length());
}

//Set the final text on TextView
mTextView.setText(spannableString);}
注意:mTextView是一个TextView对象,你想要在其中突出显示文本。

3
while (indexOfKeyword != -1) { ... }翻译为“当(某个)关键词存在时,执行...”,并在文本中突出显示第一个单词以实现该功能。 - Rany Albeg Wein
1
这个答案不会像上面列出的答案一样在处理超过5,000个字符的数据时使您的应用程序变慢。FYI - JerseyDevel
这种方法在我的自定义语音合成中效果最佳。HTML的方法无法正常工作。 - Rohaitas Tanoli
当 (indexOfKeyword >= 0) 时,修复此问题。 - Rohaitas Tanoli

2

我写了一个Kotlin方法,它会在String的所有出现中突出显示所有关键字,并返回SpannableString

fun main() {
    textView.text = highlightKeywords(
        highlightColor = ContextCompat.getColor(context, R.color.colorAccent),
        message = "Hello World, and Hello to all my Hello Friends.",
        keywords = listOf("Hello")
    )
}


fun highlightKeywords(
    highlightColor: Int,
    message: String,
    keywords: List<String>,
): SpannableString {
    val spannableString = SpannableString(message)
    keywords.forEach { keyword ->
        if (!keyword.isBlank()) {
            var startIndex = message.indexOf(keyword)

            while (startIndex >= 0) {
                spannableString.setSpan(
                    ForegroundColorSpan(highlightColor),
                    startIndex,
                    startIndex + keyword.length,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                )

                startIndex = message.indexOf(keyword, startIndex + keyword.length)
            }
        }
    }
    return spannableString
}

这是一个非常古老的问题,它涉及到使用仅限于XML的解决方案。感谢您的分享! - Slamit
从技术上讲,如果您愿意,仍然可以在XML中使用DataBinding方法来触发Kotlin扩展方法。 - Morgan Koh

1

Suraj的答案很棒并且有效,但是缺少两个组件。首先,它不会突出显示第一个单词(就像Rany所评论的那样),其次,它不区分大小写,因此在这个字符串中搜索"test"将找不到任何内容: "This is a Test"

这是我的更新答案,通过传递参数解决了这两个问题,并添加了alpha以便您可以为突出显示使用自定义颜色。请注意,重载的第一个方法是一个示例,用于返回先前方法所做的除选择第一项之外的所有内容。

    
    /**
     * Use this method to get the same return as the previous method
     */
    public static SpannableString buildHighlightString(String originalText, String textToHighlight){
        return buildHighlightString(originalText, textToHighlight, false, Color.YELLOW, 1.0F);
    }
    
    /**
     * Build a spannable String for use in highlighting text colors
     * 
     * @param originalText The original text that is being highlighted
     * @param textToHighlight The text / query that determines what to highlight
     * @param ignoreCase Whether or not to ignore case. If true, will ignore and "test" will have
     *                   the same return as "TEST". If false, will return an item as highlighted
     *                   only if it matches it case specficic.
     * @param highlightColor The highlight color to use. IE {@link Color#YELLOW} || {@link Color#BLUE}
     * @param colorAlpha Alpha to adjust how transparent the color is. 1.0 means it looks exactly
     *                   as it should normally where as 0.0 means it is completely transparent and
     *                   see-through. 0.5 means it is 50% transparent. Useful for darker colors
     */
    public static SpannableString buildHighlightString(String originalText, String textToHighlight,
                                                       boolean ignoreCase, @ColorInt int highlightColor,
                                                       @FloatRange(from = 0.0, to = 1.0) float colorAlpha){
        SpannableString spannableString = new SpannableString(originalText);
        if (TextUtils.isEmpty(originalText) || TextUtils.isEmpty(textToHighlight)) {
            return spannableString;
        }
        String lowercaseOriginalString = originalText.toLowerCase();
        String lowercaseTextToHighlight = textToHighlight.toLowerCase();
        if(colorAlpha < 1){
            highlightColor = ColorUtils.setAlphaComponent(highlightColor, ((int)(255*colorAlpha)));
        }
        //Get the previous spans and remove them
        BackgroundColorSpan[] backgroundSpans = spannableString.getSpans(0, spannableString.length(), BackgroundColorSpan.class);
        for (BackgroundColorSpan span: backgroundSpans) {
            spannableString.removeSpan(span);
        }
        //Search for all occurrences of the keyword in the string
        int indexOfKeyword = (ignoreCase)
                ? lowercaseOriginalString.indexOf(lowercaseTextToHighlight)
                : originalText.indexOf(textToHighlight);
        while (indexOfKeyword != -1) {
            //Create a background color span on the keyword
            spannableString.setSpan(new BackgroundColorSpan(highlightColor), indexOfKeyword,
                    indexOfKeyword + (textToHighlight.length()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            
            //Get the next index of the keyword
            indexOfKeyword = (ignoreCase)
                    ? lowercaseOriginalString.indexOf(lowercaseTextToHighlight, (indexOfKeyword) + textToHighlight.length())
                    : originalText.indexOf(textToHighlight, (indexOfKeyword) + textToHighlight.length());
        }
        return spannableString;
    }
    

1

在Android上工作了4年后,我可以放心地说,在XML之外没有代码是不可能的。

其他人分享的解决方案会帮助你,如果你能使用一些代码,那就好好阅读吧!

编辑:我选择了一个新的答案作为有效答案,但它需要编写自定义TextView的代码!


1

https://github.com/datanapps/HighlightedTextView

<datanapps.highlightedtextview.HighLightTextView
    android:id="@+id/tv2"
    android:layout_below="@+id/tv1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Android is an open source and Linux-based operating system for mobile devices such as smartphones and tablet computers."
    android:textColor="@color/white"
    app:fontFamily="serif"
    android:lineSpacingExtra="50sp"
    android:layout_marginTop="20dp"
    android:textSize="20sp"
    android:textAlignment="viewEnd"
    app:highLightColor="@color/blue"
    />

enter image description here


0

我理解了。我的问题是是否有其他的方法?我假设没有,但作为一个新手,我还是问一下,以防有我不知道的技巧。 - Slamit
我不明白为什么你要在TextView中设置背景,而你又想用黑色来“突出”它? ;) - finki
我想要一个带有随机背景颜色和文本内容为“背景颜色是<COLOR>”的TextView。 现在,如果背景颜色与文本颜色相反,那么我就可以读到我的文本内容。如果是深蓝色,那么很难阅读;如果是黑色,我就看不到了。 希望我已经清楚地表达了我的意思。 - Slamit

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