在ListView项目中突出显示搜索的文本

16

在此输入图片描述

我有一个ListView,并且正在使用自定义适配器显示数据。现在我想要像上面截图中那样更改搜索文本字母的颜色。

这是SearchView的代码:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.actionbar_menu_item, menu);
    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    final SearchView searchView = (SearchView) menu.findItem(R.id.action_search)
            .getActionView();
    searchView.setSearchableInfo(searchManager
            .getSearchableInfo(getComponentName()));
    searchView.setOnQueryTextListener(this);
        return super.onCreateOptionsMenu(menu);
        }

public boolean onQueryTextChange(String newText) {
    // this is adapter that will be filtered
      if (TextUtils.isEmpty(newText)){
            lvCustomList.clearTextFilter();
      }
      else{
            lvCustomList.setFilterText(newText.toString());
       }
    return false;
 }

@Override
public boolean onQueryTextSubmit(String query) {
    return false;
}

谢谢。


2
"Spannable String" 是你的最佳选择。 - Adil Soomro
@AdilSoomro 我想让字体加粗或者改变颜色,看起来像上面的截图图片(向上看)。 - Viks
Spannable String 将包含它。 - Adil Soomro
我知道Spannable String被使用了,但我不知道如何在我的情况下实现。 - Viks
3个回答

30

我假设您已经拥有一个自定义的Adapter,并已经实现了getCount()getView()方法,并已经对项目进行了筛选,您只需要加粗的部分。

要实现这一点,您需要使用SpannableString,它基本上是带有标记的文本。例如,可以使用TextAppearanceSpan来更改字体、字形、大小和颜色。

因此,您应该更新适配器的getView()方法,将其中使用textView.setText()的部分更改为类似于以下内容:

String filter = ...;
String itemValue = ...;

int startPos = itemValue.toLowerCase(Locale.US).indexOf(filter.toLowerCase(Locale.US));
int endPos = startPos + filter.length();

if (startPos != -1) // This should always be true, just a sanity check
{
    Spannable spannable = new SpannableString(itemValue);
    ColorStateList blueColor = new ColorStateList(new int[][] { new int[] {}}, new int[] { Color.BLUE });
    TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, blueColor, null);

    spannable.setSpan(highlightSpan, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    textView.setText(spannable);
}
else
    textView.setText(itemValue);

"filter.length()" 会如何处理 "endPos"? - AJW
1
@AJW 这只是计算较大字符串中子字符串的起始和结束位置。 - matiash
好的。所以,例如可以突出显示较大字符串的一部分?此外,位置索引是从0还是1开始? - AJW

2

Android搜索高亮示例 [不区分大小写顺序]

1. 搜索: [高亮特定单词]

public static SpannableStringBuilder highlightSearchText(SpannableStringBuilder fullText, String searchText) {

    if (searchText.length() == 0) return fullText;

    SpannableStringBuilder wordSpan = new SpannableStringBuilder(fullText);
    Pattern p = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE);
    Matcher m = p.matcher(fullText);
    while (m.find()) {

        int wordStart = m.start();
        int wordEnd = m.end();

        setWordSpan(wordSpan, wordStart, wordEnd);

    }

    return wordSpan;
}

2. 搜索:[高亮全词]

public static SpannableStringBuilder highlightSearchText(SpannableStringBuilder fullText, String searchText) {

    if (searchText.length() == 0) return fullText;

    final String searchBoundary = " \n()।.,;?-+!";
    char[] boundaries = searchBoundary.toCharArray();

    // highlight search text
    if (isNotEquals(searchText, boundaries)) {

        SpannableStringBuilder wordSpan = new SpannableStringBuilder(fullText);
        Pattern p = Pattern.compile(searchText, Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(fullText);
        while (m.find()) {

            int wordStart = m.start();
            while (wordStart >= 0 && isNotEquals(fullText.charAt(wordStart), boundaries)) {
                --wordStart;
            }
            wordStart = wordStart + 1;

            int wordEnd = m.end();
            while (wordEnd < fullText.length() && isNotEquals(fullText.charAt(wordEnd), boundaries)) {
                ++wordEnd;
            }

            setWordSpan(wordSpan, wordStart, wordEnd);

        }

        return wordSpan;

    } else {
        return fullText;
    }
}

private static boolean isNotEquals(String searchText, char[] boundaries) {
    for (char boundary : boundaries) {
        boolean equals = searchText.equals(String.valueOf(boundary));
        if (equals) return false;
    }
    return true;
}

private static boolean isNotEquals(char charAt, char[] boundaries) {
    for (char boundary : boundaries) {
        boolean isEquals = charAt == boundary;
        if (isEquals) return false;
    }
    return true;
}

常用方法:

private static void setWordSpan(SpannableStringBuilder wordSpan, int wordStart, int wordEnd) {
    // Now highlight based on the word boundaries
    ColorStateList redColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{0xffa10901});
    TextAppearanceSpan highlightSpan = new TextAppearanceSpan(null, Typeface.BOLD, -1, redColor, null);

    wordSpan.setSpan(highlightSpan, wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    wordSpan.setSpan(new BackgroundColorSpan(0xFFFCFF48), wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    wordSpan.setSpan(new RelativeSizeSpan(1.25f), wordStart, wordEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}

1
谢谢你的分享!它帮我省了一整天的时间。 - HuyTTQ
为什么我们需要 searchText = searchText.replace("'", ""); - HuyTTQ
@HuyTTQ 怎样调用 highlightSearchText 函数?我有一个类似的问题。你能帮我解决吗?https://stackoverflow.com/questions/60930395/how-to-highlight-the-searched-letters-in-recycler-view - user12403436

0

您可以使用CodeView库来实现此功能,您只需要在适配器中创建2个变量来存储搜索结果的模式和高亮颜色即可。

private Pattern syntaxPattern;
private Color highlightColor = Color.MAGENTA;

在适配器中创建一个用于模式和颜色的setter。
public void updateSyntaxPattern(Pattern pattern) {
    syntaxPattern = pattern;
    notifyDataSetChanged();
}

然后,在ArrayAdapter的getView方法或RecyclerAdapter中的ViewHolder中,您需要向CodeView实例添加此模式并删除旧模式(来自上一个搜索结果)

if(syntaxPattern != null) {
    codeView.resetSyntaxPatternList();
    codeView.addSyntaxPattern(syntaxPattern, highlightColor);
}

现在在SearchView的onQueryTextSubmit或onQueryTextChange中,取决于您希望高亮显示器何时工作,您将选择其中之一,您需要从搜索结果中创建模式并将其设置为适配器。

Pattern pattern = Pattern.compile(query);
adapter.updateSyntaxPattern(pattern);

它将完全按照您的要求工作

enter image description here


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