В вашем коде есть несколько соображений, которые могут привести к этому несоответствию в вашем адаптере.
Чтобы понять, как работает класс Filter, следует помнить, что согласно официальной документации :
Операции фильтрации выполняются путем вызова фильтра (java.lang.CharSequence) или фильтра (java.lang.CharSequence, android.widget.Filter.FilterListener) выполняются асинхронно . Когда эти методы вызываются, запрос на фильтрацию помещается в очередь запросов и обрабатывается позже. Любой вызов одного из этих методов отменяет любой предыдущий невыполненный запрос на фильтрацию.
1. Используйте один экземпляр Filter
Класс Filter выполняет фоновую задачу, в которой вызывается метод performFiltering()
. Чтобы избежать наложения фильтров при наборе текста, вы должны избегать использования нескольких экземпляров Filter. В вашем классе SearchGuestListAdapter:
Неправильно:
@Override
public Filter getFilter() {
// WRONG: Return new instance of Filter every time getFilter() is called
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
// Perform filtering...
return anything;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
// Update and Notify adapter...
}
};
}
Вместо этого вместо:
private final Filter filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
// Perform filtering...
return anything;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
// Update and Notify adapter...
}
};
@Override
public Filter getFilter() {
// CORRECT: Always use the same Filter instance.
return this.filter;
}
2. Не следует изменять переменные адаптера в методе executeFiltering ()
performFiltering()
, выполняемом в фоновом потоке. Здесь вы можете выполнять долгую работу, читать данные из базы данных и даже делать синхронный запрос веб-службы, если вам это нужно.
Но если вы измените здесь список адаптеров, вы вызовете несоответствие между отображением RecyclerView наэкран и содержимое списка адаптеров. Вместо этого вы должны создать временный список, который вы будете возвращать, используя FilterResults.
Исправьте executeFiltering () со следующей структурой:
@Override
protected FilterResults performFiltering(CharSequence charSequence) {
// Here is Background Thread, never alter adapter list in this method
String charString = charSequence.toString();
List<RegisterGuestList.Guest> filteredList = new ArrayList<>();
if (charString.isEmpty()) {
filteredList.addAll(mSearchGuestListResponseList);
} else {
... // fill filteredList as you did previously
}
FilterResults filterResults = new FilterResults();
filterResults.values = filteredList;
filterResults.count = filteredList.size(); // count is optional
return filterResults;
}
@Override
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
// Here is Main Thread, safe to update list and notify adapter
mSearchGuestListResponseListFiltered = (ArrayList<RegisterGuestList.Guest>) filterResults.values;
searchText = charSequence.toString();
notifyDataSetChanged();
}
3. Используйте FilterListener, чтобы получать уведомления о завершении фильтра.
В вашем onQueryTextChange()
из SearchGuestActivity
вы хотите проверить размер списка адаптеров, чтобы показать или скрыть пустое представление. Поскольку фильтр работает в фоновом потоке, вы должны выполнить вызов фильтра с помощью FilterListener , чтобы проверить пустой список.
Удалите setFilter()
метод SearchGuestListAdapter и исправьте onQueryTextChange()
SearchGuestActivity с помощьюследующая структура:
@Override
public boolean onQueryTextChange(String newText) {
mSearchGuestListAdapter.getFilter().filter(newText, new Filter.FilterListener() {
@Override
public void onFilterComplete(int count) {
if (count == 0) {
// Show empty view...
} else {
// Show recyclerView...
}
}
});
// Don't call notifyDataSetChanged() or setFilter() here!
// Adaper will notified by publishResults() method
// mSearchGuestListAdapter.setFilter(newText);
// mSearchGuestListAdapter.notifyDataSetChanged();
return false;
}