过滤后ListView不更新

Posted

技术标签:

【中文标题】过滤后ListView不更新【英文标题】:ListView Not Updating After Filtering 【发布时间】:2011-03-25 18:38:27 【问题描述】:

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

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

任何想法是什么导致了这个问题以及如何最好地解决这个问题?

【问题讨论】:

只是一个想法:我认为如果有更改,您需要取消过滤列表,然后更新列表并最后再次过滤,以使其正常工作。我知道这是一种变通方法,但总比没有好。 谢谢,BennySkogberg。问题是,一旦我对列表执行任何过滤(即使我取消过滤它 - 从而将其恢复到原始状态),任何后续修改(添加、插入、删除)都会在后台成功发生(记录数据内容显示我仍然可以从中添加/删除项目),但即使我调用 notifyDataSetChanged,它也不会反映在显示的 ListView 上。 看看这个:blogactivity.wordpress.com/2011/08/28/filterable-adapter 【参考方案1】:

我查看了 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。在您决定在列表上启用过滤并实际执行过滤之前,这一切都很好。第一次过滤会使用 mObjects 的任何内容初始化 mOriginalValues:

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。然而,由于适配器被设置为将 mObjects 视为其主要数据集,因此它会在屏幕上显示没有发生任何事情。直到您执行另一轮过滤。删除过滤器会触发:

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 所建议的]。

如果有人能对这个问题有更多的了解或确认我刚刚做了什么,我们将不胜感激。谢谢!

【讨论】:

要完成这个答案,你可以通过修改 ArrayAdapter 中的方法 sort(Comparator&lt;? super T&gt; comparator) 来解决这个问题,如果你可以访问它:删除 else 语句,Collections.sort(mObjects, comparator); 将被调用,即使在创建副本之后。 你根本不需要锁。 android 将处理过滤器的线程。您应该观看讲座“listView 的世界”。他们在那里谈论它。【参考方案2】:

如果我能很好地理解您的问题 - 您是否调用 notifyDataSetChanged() 方法?它强制列表视图重绘自身。

listAdapter.notifyDataSetChanged();

只要你做某事(例如:图像的延迟加载)就使用它来更新列表视图。

【讨论】:

是的,我仍然调用 notifyDataSetChanged 希望它能解决我的问题。但即便如此,似乎也被忽略了。我知道即使在过滤然后取消过滤以将列表恢复到其原始状态之后,我仍然能够修改列表。但是,我对适配器/列表的基础数据集所做的任何更改都不会显示在屏幕上。【参考方案3】:

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

【讨论】:

【参考方案4】:

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

public ArrayList<String> listArray = new ArrayList<>();
public ArrayList<String> tempListArray = new ArrayList<>();
public ArrayAdapter<String> tempAdapter;
public String currentFilter;

在 onCreate 中:

// Fill my ORIGINAL listArray;
listArray.add("Cookies are delicious.");

// After adding everything to listArray, I add everything to tempListArray
for (String element : listArray)
    tempListArray.add(element);


// Setting up the Adapter...
tempAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, tempListArray);
myListView.setAdapter(tempAdapter);

过滤:

// 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);
    

我用它来在我的列表中实现搜索功能。

最大的优势:您拥有更多的过滤能力:在 customFilterList() 中,您可以轻松实现正则表达式的使用或将其用作案例 敏感过滤器。

您可以调用 updateList() 而不是 notifyDataSetChanged

您可以调用 customFilterList('filter text')myListView.getFilter().filter('filter text') 而不是 /p>

目前这仅适用于一个 ListView。也许 - 通过一些工作 - 您可以将其实现为自定义数组适配器。

【讨论】:

【参考方案5】:

我在同样的问题上苦苦挣扎。然后我在 *** 上找到了this 的帖子。

在@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;

原始数据和过滤数据。

originalData 是存储传入的原始arraylist 的位置,而过滤后的数据是调用getCount 和getItem 时使用的数据集。

在我自己的 ArrayAdapter 中,当调用 getItem 时,我一直返回原始数据。因此,ListView 基本上会调用 getCount() 并查看项目数量是否与我的原始数据(不是过滤后的数据)匹配,并且 getItem 将从原始数据中获取请求的索引项目,因此完全忽略新的过滤数据集。

这就是为什么 notifyDatasetChanged 调用似乎没有更新 ListView 的原因,而实际上它只是从原始数组列表而不是过滤集不断更新。

希望这有助于将来为其他人澄清这个问题。

如果我错了,请随时纠正我,因为我每天都在学习!

【讨论】:

以上是关于过滤后ListView不更新的主要内容,如果未能解决你的问题,请参考以下文章

文本过滤后从 Android ListView/ArrayAdapter 中删除项目不起作用

自定义 listView 数组,搜索,在 NotifyDataSetChanged 上崩溃,加上 listView 没有得到更新

Android - 数据重新排序后 ListView 不更新

winform。 listview 更新数据后刷新

listview 不使用 setState 更新

过滤后的jquery mobile listview计数项目