У меня есть SearchView, настроенный на верхней панели инструментов в MainActivity. Пользователь щелкает значок поиска, а затем вводит символы поиска с экранной клавиатуры. Наблюдатель ViewModel возвращает совпадающие данные из базы данных Room, а затем запускает метод setFilter() для CardsAdapter, чтобы настроить список RecyclerView, который показывает CardViews, соответствующие строке поиска. Я настроил код Spannable в адаптере onBindViewHolder(), чтобы выделить символы, соответствующие строке поиска, для каждого возвращаемого CardView.
Код поиска возвращает правильный CardViews. Однако код Spannable не работает должным образом. Что мне здесь не хватает?
Вот результаты, которые я пытаюсь исправить:
если в SearchView введена маленькая буква t,
а) тогда выделяется только первая t (зеленым цветом). Все остальные маленькие ts для этой строки CardView EditText не выделяются. Я хотел бы, чтобы все маленькие ts были выделены.
б) ни один из элементов CardView с заглавной буквой T не выделен зеленым цветом. Я хотел бы, чтобы все буквы T в верхнем регистре также были выделены.
Вот пример CardView, показывающий две проблемы: только первая маленькая буква t выделена зеленым цветом, а буква T в верхнем регистре вообще не выделена.
если в SearchView введена заглавная буква T,
а) тогда выделена только первая Т (зеленым цветом). Все остальные прописные буквы T не выделены. Я хотел бы, чтобы все T были выделены.
б) ни один из CardViews с маленькой буквой t не выделен зеленым цветом. Я хотел бы, чтобы все маленькие ts также были выделены.
Если в onBindViewHolder() добавить строку todoHighlight = card.getTodo() с .toLowerCase(), то:
если в SearchView введена маленькая буква t,
а) тогда выделяется только первая буква T или t (зеленым цветом). Все остальные Ts или ts не выделены. Я бы хотел, чтобы они все были выделены.
б) Заглавные буквы выделены зеленым цветом, как я и хотел.
если в SearchView введена заглавная буква T,
а) ни один из элементов CardView, в которых есть маленькая буква t или заглавная буква T, не выделен зеленым цветом. Я бы хотел, чтобы они все были выделены.
Основная деятельность
...
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)