android:如何通过 RecyclerView 中的搜索修复 SpannableStrings?

问题描述

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

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

以下是我试图修复的结果:

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

    a) 然后仅突出显示一个“t”(绿色)。所有剩余的小“t” CardView EditText 行未突出显示。我希望所有小的“t”都突出显示

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

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

enter image description here

  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)

解决方法

以下代码用于在问题上突出显示多个文本: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,null);
        spannable.setSpan(highlightSpan,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...