У меня есть ListView (с (верным) setTextFilterEnabled), и пользовательский адаптер (расширяет ArrayAdapter), который я обновляю от основного потока UI каждый раз, когда новый объект добавляется/вставляется. Все хорошо работает сначала - новые объекты сразу обнаруживаются в списке. Однако это останавливает момент, я пытаюсь отфильтровать список.
При фильтрации работ, но я делаю это однажды, и все мои последующие попытки изменить содержание списка (добавьте, удалите), больше не отображаются. Я использовал Журнал, чтобы видеть, обновляются ли данные списка адаптера правильно, и это делает, но это больше не находится в синхронизации с показанным ListView.
Какие-либо идеи, что вызывает это и как лучше всего решить проблему?
Я просмотрел фактический исходный код ArrayAdapter, и похоже, что он действительно был написан таким образом.
ArrayAdapter имеет два списка для начала: mObjects и mOriginalValues. mObjects - это основной набор данных, с которым будет работать адаптер. Возьмем функцию add (), например:
public void add(T object) {
if (mOriginalValues != null) {
synchronized (mLock) {
mOriginalValues.add(object);
if (mNotifyOnChange) notifyDataSetChanged();
}
} else {
mObjects.add(object);
if (mNotifyOnChange) notifyDataSetChanged();
}
}
mOriginalValues изначально имеет значение null, поэтому все операции (добавление, вставка, удаление, очистка) по умолчанию нацелены на mObjects. Это нормально до тех пор, пока вы не решите включить фильтрацию в списке и не выполните ее. Фильтрация в первый раз инициализирует mOriginalValues с тем, что есть у mObjects:
private class ArrayFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
synchronized (mLock) {
mOriginalValues = new ArrayList<T>(mObjects);
//mOriginalValues is no longer null
}
}
if (prefix == null || prefix.length() == 0) {
synchronized (mLock) {
ArrayList<T> list = new ArrayList<T>(mOriginalValues);
results.values = list;
results.count = list.size();
}
} else {
//filtering work happens here and a new filtered set is stored in newValues
results.values = newValues;
results.count = newValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
//noinspection unchecked
mObjects = (List<T>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
mOriginalValues теперь имеет копию, ну, ну, исходных значений / элементов, поэтому адаптер может выполнять свою работу и отображать отфильтрованный список через mObjects без потери предварительно отфильтрованных данные.
Теперь простите меня (и, пожалуйста, скажите и объясните), если я ошибаюсь, но я нахожу это странным, потому что теперь, когда mOriginalValues больше не равно нулю, все последующие вызовы любой из операций адаптера будут только изменять mOriginalValues.Однако, поскольку адаптер был настроен на просмотр объектов mObject в качестве основного набора данных, на экране могло показаться, что ничего не происходит. Это до тех пор, пока вы не выполните еще один раунд фильтрации. Удаление фильтра вызывает следующее:
if (prefix == null || prefix.length() == 0) {
synchronized (mLock) {
ArrayList<T> list = new ArrayList<T>(mOriginalValues);
results.values = list;
results.count = list.size();
}
}
mOriginalValues, которые мы изменяли с момента нашего первого фильтра (хотя мы не могли видеть, что это происходит на экране), сохраняется в другом списке и копируется в mObjects, наконец, отображая сделанные изменения. Тем не менее, с этого момента так и будет: все операции будут выполняться с mOriginalValues, а изменения появятся только после фильтрации.
Что касается решения, то на данный момент я придумал либо (1) установить логический флаг, который сообщает операциям адаптера, есть ли текущая фильтрация или нет - если фильтрация завершена, скопируйте над содержимым mOriginalValues в mObjects, или (2) просто вызвать объект Filter адаптера и передать пустую строку * .getFilter (). filter ("") для принудительного применения фильтра после каждой операции [как также предлагает BennySkogberg].
Был бы очень признателен, если бы кто-нибудь мог пролить больше света на этот вопрос или подтвердить то, что я только что сделал. Спасибо!
Если я хорошо понимаю вашу проблему - вы вызываете метод notifyDataSetChanged ()? Это заставляет список перерисовываться.
listAdapter.notifyDataSetChanged();
просто используйте это всякий раз, когда вы что-то делаете (например, ленивая загрузка изображений) для обновления списка.