问题描述
我在 MainActivity 的顶部工具栏上设置了一个 SearchView。用户点击搜索图标,然后从软键盘输入搜索字符。 viewmodel 观察者从 Room 数据库返回匹配数据,然后为 CardsAdapter 运行 setFilter() 方法以设置 RecyclerView 列表,该列表显示与搜索字符串匹配的 CardView。我在适配器的 onBindViewHolder() 中设置了 Spannable 代码以突出显示与搜索字符串匹配的字符,为每个返回的 CardView。
搜索代码返回正确的 CardView。但是, Spannable 代码未按预期工作。我在这里错过了什么?
以下是我试图修复的结果:
-
如果在 SearchView 中输入了一个小的“t”,
a) 然后仅突出显示第一个“t”(绿色)。所有剩余的小“t” CardView EditText 行未突出显示。我希望所有小的“t”都突出显示。
这是一个示例 CardView,显示了两个问题:只有第一个小“t”以绿色突出显示,而大写的“T”根本没有突出显示。
在 onBindViewHolder() 中,如果我附加了“String todoHighlight = card.getTodo()”和“.toLowerCase()”,则:
-
如果在 SearchView 中输入了一个小的“t”,
a) 然后仅突出显示第一个“T”或“t”(绿色)。所有剩余的“T”或“t”都是 没有突出显示。我希望它们都突出显示。
b) 如我所愿,大写的“T”以绿色突出显示。
-
如果在 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);
}
}