Android 筛选后ListView未更新

Android 筛选后ListView未更新,android,listview,filter,filtering,android-arrayadapter,Android,Listview,Filter,Filtering,Android Arrayadapter,我有一个ListView(带有setTextFilterEnabled(true))和一个自定义适配器(extends ArrayAdapter),每当添加/插入新项时,我都会从主UI线程更新它。一开始一切正常——新项目立即出现在列表中。然而,当我尝试过滤列表时,这就停止了 过滤是有效的,但我只做了一次,以后所有修改列表内容的尝试(添加、删除)都不再显示。我使用日志查看适配器的列表数据是否得到了正确更新,确实得到了更新,但它不再与所示的ListView同步 你知道是什么导致了这个问题,以及如何最

我有一个ListView(带有setTextFilterEnabled(true))和一个自定义适配器(extends ArrayAdapter),每当添加/插入新项时,我都会从主UI线程更新它。一开始一切正常——新项目立即出现在列表中。然而,当我尝试过滤列表时,这就停止了

过滤是有效的,但我只做了一次,以后所有修改列表内容的尝试(添加、删除)都不再显示。我使用日志查看适配器的列表数据是否得到了正确更新,确实得到了更新,但它不再与所示的ListView同步


你知道是什么导致了这个问题,以及如何最好地解决这个问题吗?

如果我很了解你的问题,你会调用notifyDataSetChanged()方法吗?它强制listview重新绘制自身

listAdapter.notifyDataSetChanged();

只要在执行某些操作(例如:延迟加载图像)以更新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,因此所有操作(添加、插入、删除、清除)默认都以mobject为目标。在您决定对列表启用筛选并实际执行筛选之前,这一切都是正常的。首次筛选将使用mObjects具有的任何对象初始化MoriginalValue:

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();
        }
    }
}
私有类ArrayFilter扩展了过滤器{
@凌驾
受保护的筛选器结果执行筛选(字符序列前缀){
FilterResults results=新的FilterResults();
if(mOriginalValues==null){
已同步(mLock){
mOriginalValues=新数组列表(mObjects);
//mOriginalValues不再为空
}
}
if(prefix==null | | prefix.length()==0){
已同步(mLock){
ArrayList=新的ArrayList(MoriginalValue);
结果.值=列表;
results.count=list.size();
}
}否则{
//过滤工作在这里进行,新的过滤集存储在newValues中
results.values=新值;
results.count=newValues.size();
}
返回结果;
}
@凌驾
受保护的void publishResults(CharSequence约束、FilterResults结果){
//未检查
mObjects=(列表)results.values;
如果(results.count>0){
notifyDataSetChanged();
}否则{
notifyDataSetionValidated();
}
}
}
mOriginalValues现在拥有原始值/项的副本,因此适配器可以通过mObjects执行其工作并显示过滤列表,而不会丢失预过滤的数据

如果我的想法不正确,请原谅(请告诉并解释),但我觉得这很奇怪,因为现在mOriginalValues不再为null,所有后续对任何适配器操作的调用都只会修改mOriginalValues。然而,由于适配器被设置为将mObjects作为其主要数据集,所以在屏幕上显示什么也没有发生。直到执行另一轮过滤为止。删除筛选器会触发以下操作:

if (prefix == null || prefix.length() == 0) {
            synchronized (mLock) {
                ArrayList<T> list = new ArrayList<T>(mOriginalValues);
                results.values = list;
                results.count = list.size();
            }
        }
if(prefix==null | | prefix.length()==0){
已同步(mLock){
ArrayList=新的ArrayList(MoriginalValue);
结果.值=列表;
results.count=list.size();
}
}
mOriginalValues存储在另一个列表中,并复制到mObjects,最后显示所做的更改。我们从第一个过滤器开始就一直在修改mOriginalValues(尽管我们无法在屏幕上看到它发生)。然而,从这一点开始,情况将是这样的:所有操作都将在MoriginalValue上完成,并且只有在过滤之后才会出现更改

至于解决方案,我现在想到的是(1)放置一个布尔标志,告诉适配器操作是否有正在进行的过滤——如果过滤完成,然后将MoriginalValue的内容复制到mObjects,或者(2)简单地调用适配器的Filter对象并传递一个空字符串*.getFilter().filter(“”)在每次操作后强制执行过滤器[也由BennySkogberg建议]


如果有人能在这个问题上透露更多的信息或证实我刚才所做的事情,我将不胜感激。谢谢大家!

自2010年7月以来,该问题被报告为缺陷。看看我的

首先我想说的是,@jhie的答案非常好。然而,这些事实并没有真正解决我的问题。但借助于对内部使用机制如何工作的了解,我可以编写自己的过滤函数:

public ArrayList<String> listArray = new ArrayList<>();
public ArrayList<String> tempListArray = new ArrayList<>();
public ArrayAdapter<String> tempAdapter;
public String currentFilter;
我用它在我的列表中实现了一个搜索功能

最大的优点是:您有更强大的过滤能力:在customFilterList()中,您可以轻松实现正则表达式的用法或将其作为一个案例使用 灵敏过滤器

而不是notifyDataSetChanged,您将调用updateList()

您将调用customFilterList('filter text'),而不是myListView.getFilter().filter('filter text')


目前,这只适用于一个ListView。也许,只要做一点工作,您就可以将其实现为一个自定义阵列适配器。

我也遇到了同样的问题。然后我在StackOverflow上找到了一个帖子

在@MattDavis发布的答案中,传入的arraylist被分配给2个不同的arraylist

 public PromotionListAdapter(Activity a, ArrayList<HashMap<String, String>> d) 
{
    activity = a;
    inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    imageLoader = new ImageLoader(activity.getApplicationContext());

    //To start, set both data sources to the incoming data
    originalData = d;
    filteredData = d;
}
公共促销列表适配器(活动a,ArrayList d) { 活动=a; 充气器=(LayoutInflater)activity.getSystemService(Context.LAYOUT\u充气器\u SERVICE); imageLoader=新的imageLoader(activity.getApplicationContext()); //首先,将两者都设置为
// Defining filter functions
public void customFilterList(String filter){
        tempListArray.clear();
        for (String item : listArray){
            if (item.toLowerCase().contains(filter.toLowerCase())){
                tempListArray.add(item);
            }
        }
        currentFilter = filter;
        tempAdapter.notifyDataSetChanged();
    }
    public void updateList(){
        customFilterList(currentFilter);
    }
 public PromotionListAdapter(Activity a, ArrayList<HashMap<String, String>> d) 
{
    activity = a;
    inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    imageLoader = new ImageLoader(activity.getApplicationContext());

    //To start, set both data sources to the incoming data
    originalData = d;
    filteredData = d;
}