显示空视图后过滤器列表未重新出现

Posted

技术标签:

【中文标题】显示空视图后过滤器列表未重新出现【英文标题】:Filter list not reappearing after empty view shown 【发布时间】:2015-09-09 23:01:44 【问题描述】:

由于某种原因,在显示没有匹配项的空视图后,从搜索视图中删除所有文本后,我的列表视图不会重新出现。

在我的适配器类中,mData.clear(); 中的 AFAIK .clear() 需要更改为其他内容,但我不知道该怎么做。

ItemListAdapter 类

public class ItemListAdapter extends BaseAdapter implements Filterable 

    private List<Victoria> mData;
    private List<Victoria> mFilteredData;
    private LayoutInflater mInflater;
    private ItemFilter mFilter;

    public ItemListAdapter (List<Victoria> data, Context context) 
        mData = data;
        mFilteredData = new ArrayList(mData);
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    

    @Override
    public int getCount() 
        return mFilteredData.size();
    

    @Override
    public String getItem(int position) 
        return mFilteredData.get(position).getItem();
    

    @Override
    public long getItemId(int position) 
        return position;
    

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 

        ViewHolder holder;
        if (convertView == null) 
           convertView = mInflater.inflate(R.layout.item_row, parent, false);
            holder = new ViewHolder();

            holder.title = (TextView) convertView.findViewById(R.id.item_title);
            holder.description = (TextView) convertView.findViewById(R.id.item_description);

            convertView.setTag(holder);
         else 
            holder = (ViewHolder) convertView.getTag();
        

        holder.title.setText(mFilteredData.get(position).getItem());
        holder.description.setText(mFilteredData.get(position).getItemDescription());

        return convertView;
    

    @Override
    public Filter getFilter() 
        if (mFilter == null) 
            mFilter = new ItemFilter();
        
        return mFilter;
    

    /**
     * View holder
     */
    static class ViewHolder 
        private TextView title;
        private TextView description;
    

    /**
     * Filter for filtering list items
     */
    /**
     * <p>An array filter constrains the content of the array adapter with
     * a prefix. Each item that does not start with the supplied prefix
     * is removed from the list.</p>
     */
    private class ItemFilter extends Filter 
        @Override
        protected FilterResults performFiltering(CharSequence constraint) 
            FilterResults results = new FilterResults();

            if (TextUtils.isEmpty(constraint)) 
                results.count = mData.size();
                results.values = mData;
             else 
                //Create a new list to filter on
                List<Victoria> resultList = new ArrayList<Victoria>();
                for (Victoria str : mData) 
                    if (str.getItemDescription().toLowerCase().contains(constraint.toString().toLowerCase())) 
                        resultList.add(str);
                    
                
                results.count = resultList.size();
                results.values = resultList;
            
            return results;
        

        /**
         * Runs on ui thread
         * @param constraint the constraint used for the result
         * @param results the results to display
         */
        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) 
            if (results.count == 0) 
                //Make list invisible
                //Make text view visible
                mFilteredData.clear();
                notifyDataSetInvalidated();
             else 
                mFilteredData = (ArrayList<Victoria>)results.values;
                notifyDataSetChanged();
            
        
    

过滤前

使用无效字符过滤后

清除搜索视图并尝试再次显示列表后

【问题讨论】:

您能否通过替换“mData.clear();”来更改逻辑到“mFilteredData.clear();”在 publishResults() 方法中? @RaghuRamiReddy 刚刚做到了 你测试过吗。有效吗? @RaghuRamiReddy 不,它仍然没有再次显示列表。您可以使用我的代码并发布您建议的代码作为答案吗?这会很有帮助。 【参考方案1】:

几个小修复应该让事情适合您。首先,mDatamFilteredData 数据在任何时候都应该是两个不同的实例。在构造函数中,让mFilteredData 成为从mData 复制的新实例

mFilteredData = new ArrayList(mData);

另外在Filter中,你需要更新对应的代码如下:

if (TextUtils.isEmpty(constraint)) 
    results.count = mData.size();
    results.values = new ArrayList(mData);

那么你的getView() 方法应该是从mFilteredData 拉取而不是mData。不过,理想情况下,getView() 方法应该使用getItem(position) 方法来访问数据。这样,如果您更改从何处访问数据的实现细节,您就不必记住单独更新 getView() 方法。

最后作为旁注,您的过滤代码仅因为适配器不会突变mFilteredDatamData 而起作用。通常,必须大量synchronize 过滤进程和适配器的其他部分,因为performFiltering() 发生在后台线程上。如果您计划在构建后添加对修改适配器数据的支持...那么您需要开始添加 synchronized 块。

【讨论】:

您能看一下更新的代码并为我的班级提供代码建议吗? 您的getView() 仍然没有从mFilteredData 拉出 刚刚看到mFilteredData 实例与mData 相同的地方。更新了我的答案。【参考方案2】:

您正在修改包含原始数据的数组,当 de Adapter 重新创建列表时,该数组没有数据。

正确的做法是创建原始数组的副本,并在每次过滤列表时将副本作为结果返回,而原始数据保持不变。在ArrayAdapter 实现中可以找到一个很好的例子。见源代码here。

为了以防万一,我将在此处包含源代码的副本:

/**
 * <p>An array filter constrains the content of the array adapter with
 * a prefix. Each item that does not start with the supplied prefix
 * is removed from the list.</p>
 */
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);
            
        
        if (prefix == null || prefix.length() == 0) 
            ArrayList<T> list;
            synchronized (mLock) 
                list = new ArrayList<T>(mOriginalValues);
            
            results.values = list;
            results.count = list.size();
         else 
            String prefixString = prefix.toString().toLowerCase();
            ArrayList<T> values;
            synchronized (mLock) 
                values = new ArrayList<T>(mOriginalValues);
            
            final int count = values.size();
            final ArrayList<T> newValues = new ArrayList<T>();
            for (int i = 0; i < count; i++) 
                final T value = values.get(i);
                final String valueText = value.toString().toLowerCase();
                // First match against the whole, non-splitted value
                if (valueText.startsWith(prefixString)) 
                    newValues.add(value);
                 else 
                    final String[] words = valueText.split(" ");
                    final int wordCount = words.length;
                    // Start at index 0, in case valueText starts with space(s)
                    for (int k = 0; k < wordCount; k++) 
                        if (words[k].startsWith(prefixString)) 
                            newValues.add(value);
                            break;
                        
                    
                
            
            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();
        
    

【讨论】:

旁注,ArrayFilterArrayAdapter 实现有很多错误,我建议不要使用它。 jaysoyer.com/2014/07/filtering-problems-arrayadapter 我明白了。我从来没有遇到过这个问题,因为我通常不会在同一个 Activity 中更改适配器数据。

以上是关于显示空视图后过滤器列表未重新出现的主要内容,如果未能解决你的问题,请参考以下文章

过滤列表视图后的项目混乱

将过滤器应用于列表后,TextWatcher 和 ListView 元素的位置出现问题

在搜索期间重新加载 UITableView

如何仅显示与视图 2 中关联的节点的分类术语?

Angularjs模板默认值,如果绑定空/未定义(使用过滤器)

数据表过滤器下拉列表未正确显示