Android - 如何在TextView中高亮显示一个单词?

36
我有一个数据库搜索查询,它在数据库中搜索用户输入的单词并返回一个Cursor。
在我的ListActivity中,我有一个ListView,用于保存这些项(Cursor项)。ListView的项布局基本上是一个TextView。也就是说,ListView将是一个TextView列表。
我希望能够在TextView中突出显示搜索词。我的意思是,可以通过不同的颜色或背景颜色等方式使搜索词与其他文本不同。
是否可能实现?如何实现?
更新:
cursor = myDbHelper.search(term);  //term: a word entered by the user.
cursor.moveToFirst();
String[] columns = {cursor.getColumnName(1)}; 
int[] columnsLayouts = {R.id.item_title}; //item_title: the TextView holding the one raw
ca = new SimpleCursorAdapter(this.getBaseContext(), R.layout.items_layout, cursor,columns , columnsLayouts);
lv = getListView();
lv.setAdapter(ca);

对于 @Shailendra :search() 方法将返回一些标题。我希望可以突出显示与 term 单词匹配的这些标题中的单词。现在我希望这已经很清楚了。

9个回答

45

在textView中插入围绕单词的HTML代码并设置颜色。

例如:

String newString = oldString.replaceAll(textToHighlight, "<font color='red'>"+textToHighlight+"</font>");
textView.setText(Html.fromHtml(newString));

问题在于它不是一个简单的 TextView 来保存文本。它是一个 ListView,用于保存查询结果(可能是一个或多个结果,因此可能有一个或多个 TextView)。所以,我怀疑我能否通过这种方式更改列表中的文本呢? - iTurki
所以,我看不出关于ListView内部文本的任何问题......你能详细说明一下你的问题吗???? - Shailendra Singh Rajawat
1
在这里,您正在使用SimpleCursorAdapter,它将把相应列的文本设置为TextView R.id.item_title。因此,制作一个扩展SimpleCursorAdapter的customAdapter。将String term传递给它,并将上面的replaceAll代码编写到getView重写方法中。 - Shailendra Singh Rajawat
我会尝试并回来这里 :) - iTurki
如果用户输入HTML标签,他们不会对文本进行有趣的操作吗? - CodeGuru
在Android 10和11中工作正常,但在Android 7中无法工作。 - Abu Saeed

25
TextView textView = (TextView)findViewById(R.id.mytextview01);

//use a loop to change text color
Spannable WordtoSpan = new SpannableString("partial colored text");        
WordtoSpan.setSpan(new ForegroundColorSpan(Color.BLUE), 2, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(WordtoSpan);

数字2和4是文本着色的起始/停止索引,在此示例中,“rti”将被着色。

因此,您基本上只需在标题中查找搜索词的起始索引:

int startIndex = titleText.indexOf(term);
int stopIndex = startIndex + term.length();

然后将数字2和4替换为索引,"partial colored text" 替换为你的标题字符串。

来源:https://dev59.com/UGPVa4cB1Zd3GeqP1wQu#10279703


9

更简单的方法

您可以使用Spannable类来突出显示/格式化文本的一部分。

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

输出

这里是方法。

 /**
     * 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);
            }
        }
    }

您可以查看此答案,了解可点击的高亮文本。


非常简单。谢谢。 - Numan Karaaslan

5

我知道这是一个老问题,但我已经创建了一种方法来突出显示字符串\段落中的重复单词。

private Spannable highlight(int color, Spannable original, String word) {
    String normalized = Normalizer.normalize(original, Normalizer.Form.NFD)
            .replaceAll("\\p{InCombiningDiacriticalMarks}+", "");

    int start = normalized.indexOf(word);
    if (start < 0) {
        return original;
    } else {
        Spannable highlighted = new SpannableString(original);
        while (start >= 0) {
            int spanStart = Math.min(start, original.length());
            int spanEnd = Math.min(start+word.length(), original.length());

            highlighted.setSpan(new ForegroundColorSpan(color), spanStart,
                    spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);

            start = normalizedText.indexOf(word, spanEnd);
        }
        return highlighted;
    }
}

用法:

textView.setText(highlight(primaryColor, textAll, wordToHighlight));

如果我需要添加内容,请不要将跨度单词toString转换,否则它会失去所有的高亮。 - Peterstev Uremgba

5

根据之前的回答,我开发了以下函数,您可以复制/粘贴它

 private void highlightMask(TextView textView, String text, String mask) {
            boolean highlightenabled = true;
            boolean isHighlighted = false;

            if (highlightenabled) {
                if (!TextUtils.isEmpty(text) && !TextUtils.isEmpty(mask)) {
                    String textLC = text.toLowerCase();
                    mask = mask.toLowerCase();

                    if (textLC.contains(mask)) {
                        int ofe = textLC.indexOf(mask, 0);
                        Spannable wordToSpan = new SpannableString(text);
                        for (int ofs = 0; ofs < textLC.length() && ofe != -1; ofs = ofe + 1) {
                            ofe = textLC.indexOf(mask, ofs);
                            if (ofe == -1) {
                                break;
                            } else {
                                // set color here
                                wordToSpan.setSpan(new BackgroundColorSpan(0xFFFFFF00), ofe, ofe + mask.length(),
                                                   Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                                textView.setText(wordToSpan, TextView.BufferType.SPANNABLE);
                                isHighlighted = true;
                            }
                        }

                    }
                }
            }

            if (!isHighlighted) {
                textView.setText(text);
            }
        }

2

我还没有尝试过,但这看起来很有前途:

http://developer.android.com/reference/android/text/SpannableString.html
http://developer.android.com/guide/topics/resources/string-resource.html

public final void setText (CharSequence text)

自API Level 1起,设置TextView的字符串值。TextView不接受类似HTML的格式,这可以通过XML资源文件中的文本字符串来实现。要为您的字符串设置样式,请将android.text.style.*对象附加到SpannableString上,或者参见“可用资源类型”文档中有关在XML资源文件中设置格式化文本的示例。

http://developer.android.com/reference/android/widget/TextView.html


1
我知道这个帖子已经很老了,但如果有人想在TextView中突出显示字符串,我创建了一个库可以做到这一点。这是我在stackoverflow上回答问题的第一个答案,因为我刚刚加入,希望它格式正确并且相关。它使用SpannableString,并将定位您指定的字符串的所有出现。此外,内置了自定义ClickableSpan,如果需要,可以设置文本点击的监听器。
Linker 轻量级Android库,用于在TextView内突出显示字符串(不区分大小写),可选回调函数。
语言:Java
MinSDK:17
其功能图像和所有代码可以在这里找到。 JavaDocs 要将其引入Android项目,请实现构件:
在项目级别的build.gradle中
    allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }

在应用级别的build.gradle文件中。
       dependencies {
            implementation 'com.github.Gaineyj0349:Linker:1.2'
    }

使用方法:

1 - 使用TextView构建一个Linker对象:

        Linker linker = new Linker(textView);

2-添加一个字符串数组或列表,用于在textview的文本中进行突出显示:

        ArrayList<String> list = new ArrayList<>();
        list.add("hello");
        list.add("world");
        linker.addStrings(list);

AND/OR(与/或)
        String[] words = new String[]{"One", "Two", "Three"};
        linker.addStrings(words);

3 - 添加回调函数(这是可选的):

       linker.setListener(new LinkerListener() {
            @Override
            public void onLinkClick(String charSequenceClicked) {

                // charSequenceClicked is the word that was clicked

                Toast.makeText(MainActivity.this, charSequenceClicked, Toast.LENGTH_SHORT).show();
            }
        });

4 - 调用链接器的更新方法来提交定制并推出设置。

        linker.update();

你总是可以将字符串添加到链接器对象中,只需确保在刷新跨度之后调用更新方法。

    linker.addStrings("yoda");
    linker.update();

如果您需要一个新的板子并且具有相同的链接器对象,请调用
       linker.clearLinksList()

您可以自定义链接的样式:
1 - 自定义所有链接的颜色:
      linker.setAllLinkColors(Color.BLUE);

2 - 自定义链接下划线:

      linker.setAllLinkUnderline(false);

3 - 如果您希望为某个字符串自定义颜色或下划线设置(请注意,该字符串必须已添加到链接器中):

      linker.setLinkColorForCharSequence("world", Color.MAGENTA);
      linker.setUnderlineModeForCharSequence("world", true);

4 - 如果您希望为每个单词使用不同的设置,则还可以将链接器对象提供的列表或数组LinkProfiles:

        ArrayList<LinkProfile> profiles = new ArrayList<>();
        profiles.add(new LinkProfile("hello world",
                Color.GREEN, false));
        profiles.add(new LinkProfile("goodbye cruel world",
                Color.RED, false));
        profiles.add(new LinkProfile("Whoa awesome!",
                Color.CYAN, true));


        linker.addProfiles(profiles);

请记得在链接器对象添加任何内容后调用.update()方法。

请注意,该库将处理诸如添加两个相同单词或相同单词部分等微妙问题。例如,如果"helloworld"和"hello"是添加到链接器中的两个单词,则当它们在相同的字符范围内时,"helloworld"将优先于"hello"。链接器将按照较大的单词排序,并跟踪所有跨度以链接它们 - 避免重复问题以及交叉跨度。

根据MIT许可证授权。


1

试试这个库 Android TextHighlighter

实现

TextView.setText() 方法的参数可以是 Spannable 而不仅仅是 CharacterSequence。 SpannableString 有一个方法 setSpan(),允许应用样式。

查看直接子类列表:https://developer.android.com/reference/android/text/style/CharacterStyle.html

  • 在 "Hello, World" 中给单词 "Hello" 设置背景色和前景色的示例
Spannable spannable = new SpannableString("Hello, World");
// setting red foreground color
ForegroundSpan fgSpan = new ForegroundColorSpan(Color.red);
// setting blue background color
BackgroundSpan bgSpan = new BackgroundColorSPan(Color.blue);

// setSpan requires start and end index
// in our case, it's 0 and 5
// You can directly set fgSpan or bgSpan, however,
// to reuse defined CharacterStyle, use CharacterStyle.wrap()
spannable.setSpan(CharacterStyle.wrap(fgSpan), 0, 5, 0);
spannable.setSpan(CharacterStyle.wrap(bgSpan), 0, 5, 0);

// apply spannableString on textview
textView.setText(spannable);

1
要使用此库,请在gradle中添加以下依赖:compile 'com.xeoh.android:text-highlighter:1.0.1'(但您的示例代码并不适用于此库) - gregn3

1
如果您的字符串是静态的,可以在XML字符串中这样做。
<string name="my_text">This text is <font color='red'>red here</font></string>

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