问题描述
我有一个 ListView(带有 setTextFilterEnabled(true))和一个自定义适配器(扩展 ArrayAdapter),每当添加/插入新项目时,我都会从主 UI 线程更新它们.一开始一切正常——新项目立即出现在列表中.然而,这在我尝试过滤列表的那一刻停止了.
I have a ListView (with setTextFilterEnabled(true)) and a custom adapter (extends ArrayAdapter) which I update from the main UI thread whenever a new item is added/inserted. Everything works fine at first--new items show up in the list immediately. However this stops the moment I try to filter the list.
过滤工作,但我做了一次,我所有成功的尝试修改列表的内容(添加,删除)不再显示.我使用 Log 来查看适配器的列表数据是否正确更新,确实如此,但它不再与显示的 ListView 同步.
Filtering works, but I do it once and all of my succeeding attempts to modify the contents of the list (add, remove) don't display anymore. I used the Log to see if the adapter's list data gets updated properly, and it does, but it's no longer in sync with the ListView shown.
任何想法是什么导致了这个问题以及如何最好地解决这个问题?
Any ideas what's causing this and how best to address the issue?
推荐答案
我查看了 ArrayAdapter 的实际源代码,看起来它实际上是按照这种方式编写的.
I went through ArrayAdapter's actual source code and it looks like it was actually written to behave that way.
ArrayAdapter 有两个列表开始:mObjects 和 mOriginalValues.mObjects 是适配器将使用的主要数据集.以 add() 函数为例:
ArrayAdapter has two Lists to begin with: mObjects and mOriginalValues. mObjects is the primary data set that the adapter will work with. Taking the add() function, for example:
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,因此默认情况下所有操作(添加、插入、删除、清除)都针对 mObject.这一切都很好,直到您决定在列表上启用过滤并实际执行过滤.第一次过滤用 mObjects 所拥有的任何东西初始化 mOriginalValues:
mOriginalValues is initially null so all operations (add, insert, remove, clear) are by default targeted to mObjects. This is all fine until the moment you decide to enable filtering on the list and actually perform one. Filtering for the first time initializes mOriginalValues with whatever mObjects has:
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 now has a copy of, well, the original values/items, so the adapter could do its work and display a filtered list thru mObjects without losing the pre-filtered data.
如果我的想法不正确,请原谅我(请告诉并解释),但我觉得这很奇怪,因为现在 mOriginalValues 不再为 null,对任何适配器操作的所有后续调用都只会修改 mOriginalValues.但是,由于适配器设置为将 mObjects 视为其主要数据集,因此屏幕上会显示没有发生任何事情.直到您执行另一轮过滤.删除过滤器会触发:
Now forgive me (and please do tell and explain) if my thinking is incorrect but I find this weird because now that mOriginalValues is no longer null, all subsequent calls to any of the adapter operations will only modify mOriginalValues. However since the adapter was set up to look at mObjects as its primary data set, it would appear on screen that nothing is happening. That is until you perform another round of filtering. Removing the filter triggers this:
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上进行,只有过滤后才会出现变化.
mOriginalValues, which we've been modifying since our first filter (although we couldn't see it happening on screen) is stored in another list and copied over to mObjects, finally displaying the changes made. Nevertheless it will be like this from this point onwards: all operations will be done on mOriginalValues, and changes will only appear after filtering.
至于解决方案,我目前想出的要么是 (1) 放置一个布尔标志,该标志告诉适配器操作是否正在进行过滤——如果过滤完成,则复制将 mOriginalValues 的内容转换为 mObjects,或 (2) 简单地调用适配器的 Filter 对象并传递一个空字符串 *.getFilter().filter("") 以在每次操作后强制过滤器 [正如 BennySkogberg 所建议的].
As for the solution, what I've come up with at the moment is either (1) to put a boolean flag which tells the adapter operations if there is an ongoing filtering or not--if filtering is complete, then copy over the contents of mOriginalValues to mObjects, or (2) to simply call the adapter's Filter object and pass an empty string *.getFilter().filter("") to force a filter after every operation [as also suggested by BennySkogberg].
如果有人能对这个问题有更多的了解或确认我刚刚做了什么,我们将不胜感激.谢谢!
It would be highly appreciated if anybody can shed some more light on this issue or confirm what I just did. Thank you!
这篇关于过滤后列表视图不更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!