过滤后ListView不更新

时间:2010-08-05 12:07:30

标签: android listview filter filtering android-arrayadapter

我有一个ListView(带有setTextFilterEnabled(true))和一个自定义适配器(extends ArrayAdapter),每当添加/插入新项目时,我都会从主UI线程更新。一切都工作正常 - 新项目立即显示在列表中。但是,当我尝试过滤列表时,这就停止了。

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

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

5 个答案:

答案 0 :(得分:16)

我查看了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,因此所有操作(add,insert,remove,clear)默认都是针对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不再为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();
            }
        }

mOriginalValues,我们自第一个过滤器以来一直在修改(虽然我们看不到它在屏幕上发生)存储在另一个列表中并复制到mObjects,最后显示所做的更改。不过从这一点开始它将是这样的:所有操作都将在mOriginalValues上完成,并且只有在过滤后才会出现更改。

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

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

答案 1 :(得分:0)

如果我理解你的问题 - 你是否调用notifyDataSetChanged()方法?它强制listview重绘自己。

listAdapter.notifyDataSetChanged();

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

首先我想提一下,@ 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(&#39;过滤器文本&#39)

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

答案 4 :(得分:0)

我正在努力解决同样的问题。然后我在StackOverflow上发现了this帖子。

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

 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和filteredData。

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

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

这就是为什么似乎notifyDatasetChanged调用没有更新ListView,实际上它只是保持从原始arraylist而不是过滤集更新。

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

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