【问题标题】:android: how to fix SpannableStrings from search in RecyclerView?android:如何从 RecyclerView 中的搜索修复 SpannableStrings?
【发布时间】:2021-04-25 19:03:10
【问题描述】:

我在 MainActivity 的顶部工具栏上设置了一个 SearchView。用户点击搜索图标,然后从软键盘输入搜索字符。 ViewModel 观察者从 Room 数据库返回匹配数据,然后为 CardsAdapter 运行 setFilter() 方法来设置 RecyclerView 列表,该列表显示与搜索字符串匹配的 CardViews。我在 Adapter 的 onBindViewHolder() 中设置了 Spannable 代码,为每个返回的 CardView 突出显示与搜索字符串匹配的字符。

搜索代码返回了正确的 CardViews。但是,Spannable 代码未按预期工作。我在这里错过了什么?

以下是我正在尝试解决的结果:

  1. 如果在 SearchView 中输入了一个小“t”,

    a) 那么只有第一个“t”被突出显示(绿色)。所有剩余的小“t” CardView EditText 行未突出显示。我想突出显示所有小“t”。

    b) 没有大写“T”的 CardView 以绿色突出显示。我想 所有大写的“T”也会被突出显示。

这是一个显示两个问题的示例 CardView:只有第一个小“t”以绿色突出显示,而大写“T”根本不突出显示。

  1. 如果在 SearchView 中输入了大写“T”,

    a) 那么只有第一个“T”被突出显示(绿色)。所有剩余的大写“T”都不是 突出显示。我希望突出显示所有“T”。

    b) 没有一个带有小“t”的 CardView 以绿色突出显示。我想要所有 的小“t”也会被突出显示。

在 onBindViewHolder() 中,如果我将“String todoHighlight = card.getTodo()”附加到“.toLowerCase()”,则:

  1. 如果在 SearchView 中输入了一个小“t”,

    a) 那么只有第一个“T”或“t”被突出显示(绿色)。所有剩余的“T”或“t”都是 没有突出显示。我希望它们都突出显示。

    b) 如我所愿,大写的“T”以绿色突出显示。

  2. 如果在 SearchView 中输入了大写的“T”,

    a) 没有一个带有小“t”或大写“T”的 CardView 以绿色突出显示。我希望它们都突出显示。

    主活动

    ...

     mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
             EditText mSearchEditText = mSearchView.findViewById(androidx.appcompat.R.id.search_src_text);
             mSearchEditText.setInputType(android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
             // use the link to EditText of the SearchView so that a
             // TextWatcher can be used.
             mSearchEditText.addTextChangedListener(new TextWatcher() {
    
         @Override
         public void afterTextChanged(Editable s) {
    
             if (s.toString().length() > 0) {
    
                 String queryText = "%" + s.toString() + "%";
    
                 mQuickcardViewModel.searchQuery(queryText).observe(MainActivity.this, searchQuickcards -> {
    
                     searchList = searchQuickcards;
                     if (!mSearchView.isIconified() && searchList.size() > 0) {
                         cardsAdapter.setFilter(searchList, s.toString());
                     }
                 });    
    

    卡片适配器

    ...

     public void setFilter(List<card> searchCards, String searchString) {
         mListItems = new ArrayList<>();
         mListItems.clear();
         mListItems.addAll(searchCards);
         this.searchString = searchString;
         notifyDataSetChanged();
     }
    
     public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
    
         ...
         // also tried "String todoHighlight = card.getTodo().toLowerCase();"
         String todoHighlight = card.getTodo();
    
         if (searchString != null && !searchString.isEmpty() && todoHighlight.contains(searchString)) {
    
             int todoStartPos = todoHighlight.indexOf(searchString);
             int todoEndPos = todoStartPos + searchString.length();
             Spannable spanString2 = new SpannableString(itemHolder.cardBlankText2.getText());
    
             if (spanString2 != null) {
                 spanString2.removeSpan(itemHolder.getForegroundColorSpan());
                 itemHolder.cardBlankTextNumstotal.setText(spanString2);
                 spanString2.setSpan(new ForegroundColorSpan(Color.GREEN), todoStartPos, todoEndPos,
                     Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                 itemHolder.cardBlankText2.setText(spanString2);
             }
         }
    

修改后的代码有时可以工作,有时会崩溃:

in onBindViewHolder():

String todoSearchHighlight = mListItems.get(position).getTodo();
Spannable spannable = new SpannableString(todoSearchHighlight);
if (searchString != null && !TextUtils.isEmpty(searchString)) {
    highLightText(spannable, todoSearchHighlight, 0);
    itemHolder.cardBlankText2.setText(spannable);
}
else { // searchString == null
    if (spannable != null) {
    spannable.removeSpan(itemHolder.getForegroundColorSpan());
    itemHolder.cardBlankText2.setText(todoSearchHighlight);
    }
}

private void highLightText(Spannable spannable, String string, int start) {

    int startPos = start + string.substring(start).toLowerCase(Locale.US).indexOf(searchString.toLowerCase(Locale.US));
    int endPos = startPos + searchString.length();
    if (string.substring(endPos).toLowerCase(Locale.US).contains(searchString.toLowerCase(Locale.US))){
        highLightText(spannable, string, endPos);
    }
    ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.GREEN);
    spannable.setSpan(foregroundColorSpan, startPos, endPos, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
     

执行以下答案后出现 logcat 错误:

java.lang.IndexOutOfBoundsException: setSpan (-1 ... 0) 在 0 之前开始 在 android.text.SpannableStringInternal.checkRange(SpannableStringInternal.java:442) 在 android.text.SpannableStringInternal.setSpan(SpannableStringInternal.java:163) 在 android.text.SpannableStringInternal.setSpan(SpannableStringInternal.java:152) 在 android.text.SpannableString.setSpan(SpannableString.java:46) 在 com.todo.quickcards.adapter.CardsAdapter.highLightText(CardsAdapter.java:665) 在 com.todo.quickcards.adapter.CardsAdapter.onBindViewHolder(CardsAdapter.java:643)

【问题讨论】:

    标签: android android-recyclerview spannable


    【解决方案1】:

    以下代码用于突出显示问题中的多个文本:Highlight filtered text in recyclerView

    @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        if (!filterPattern.equals("")) {
            String tmpCname = data.get(position);
    
            //int startPos = tmpCname.toLowerCase(Locale.US).indexOf(filterPattern.toLowerCase(Locale.US));
            //int endPos = startPos + filterPattern.length();
            //Spannable spannable = new SpannableString(tmpCname);
            //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);
    
            Spannable spannable = new SpannableString(tmpCname);
            highLightText(spannable, tmpCname, 0);
    
            holder.mTitle.setText(spannable);
        } else {
            holder.mTitle.setText(data.get(position));
        }
    }
    
    private void highLightText(Spannable spannable, String string, int start) {
        int startPos = start + string.substring(start).toLowerCase(Locale.US).indexOf(filterPattern.toLowerCase(Locale.US));
        if (startPos > -1) {
            int endPos = startPos + filterPattern.length();
            if (string.substring(endPos).toLowerCase(Locale.US).contains(filterPattern.toLowerCase(Locale.US))){
                highLightText(spannable, string, endPos);
            }
            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);
        }
    }
    

    【讨论】:

    • 干得好,效果很好!答案被接受并赞成,干杯。
    • 我对另一件事感到好奇:我试图将 Spannable 的创建移到 ViewHolder 中,然后从 onBindViewHolder 中获取对它的引用。这样,每次在 onBindViewHolder 中更新视图时,就不必重新创建 Spannable。但是该项目因“java.lang.IndexOutOfBoundsException: setSpan (9 ... 10) ends beyond length 4” logcat 而崩溃。我还尝试在 ViewHolder 中使用 SpannableString。关于如何将 Spannable 放入 ViewHolder 的任何想法?
    • 我不明白你的想法。我在 onBindViewHolder 内部尝试了spannable = new SpannableString(tmpCname);,而 spannable 在其他地方声明(在 ViewHolder 内部或适配器中的全局),但没有错误。请用您测试过的代码更新问题。
    • 项目正在崩溃。调用 setSpan () 时出现 IndexOutofBoundsException。往上看。它只是有时会发生,有时会完美突出显示正在搜索的字母。我把logcat放在上面。有什么想法吗?
    • 我认为过滤列表是一个只有项带有搜索词的列表。但是从您的 setFilter() 和原始 onBindViewHolder() 来看,您的过滤列表是一个包含 all 项并突出显示包含搜索词。因此,我的原始答案在不包含搜索词的项目上崩溃(start = 0 and substring() = -1, so startPos = -1 that crash) 。做一个简单的检查可以避免崩溃。
    猜你喜欢
    • 2020-11-18
    • 2020-04-27
    • 2021-05-16
    • 1970-01-01
    • 1970-01-01
    • 2020-02-28
    • 2018-06-17
    • 2019-06-12
    • 2021-03-26
    相关资源
    最近更新 更多