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