javaandroidxmlandroid-activityandroid-listfragment

List view not returning to original state after clearing search


I'm trying to get my list to show all my items again whenever I cancel a search from my search view but for some strange reason, the list gets stuck with the results only from the previous search. Does anyone know what is wrong with my code and how to fix this? I believe something is wrong with the filter related code but I don't know what it is.

FilterListFragment.java

public class ItemListAdapter extends BaseAdapter implements Filterable {

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

    public ItemListAdapter (List<Item> data, Context context) {
        mData = data;
        mFilteredData = data;
        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(mData.get(position).getItem());
        holder.description.setText(mData.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
     */
    private class ItemFilter extends Filter {

        /**
         * Invoked on a background thread.  This is where all the filter logic should go
         * @param constraint the constraint to filter on
         * @return the resulting list after applying the constraint
         */
        @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<Item> resultList = new ArrayList<Item>();
                for (Item str : mData) {
                    if (str.getItem().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) {
                notifyDataSetInvalidated();
            } else {
                mFilteredData = (ArrayList<Item>)results.values;
                notifyDataSetChanged();
            }
        }
    }
}

List in normal state enter image description here

List in filtered state enter image description here


Solution

  • You are operating on the original data instead of filtered data. You should maintain a reference to original data and use the filtered data for all other purposes. So that the original data is displayed when search is cleared.

    Replace all usages of mData with mFilteredData as below and only use the original data to generate the filtered data:

    private List<String> mData;
    private List<String> mFilteredData;
    private LayoutInflater mInflater;
    private ItemFilter mFilter;
    
    public ItemListAdapter (List<String> data, Context context) {
        mData = data;
        mFilteredData = data;
        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);
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    
        String strItem = mFilteredData.get(position);
        ViewHolder holder;
        if (convertView == null) {
           convertView = mInflater.inflate(R.layout.item_row, parent, false);
    
            holder = new ViewHolder();
            holder.mTvItem = (TextView) convertView.findViewById(R.id.tv_item);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
    
        holder.mTvItem.setText(strItem);
    
        return convertView;
    }
    
    @Override
    public Filter getFilter() {
        if (mFilter == null) {
            mFilter = new ItemFilter();
        }
        return mFilter;
    }
    
    /**
     * View holder
     */
    static class ViewHolder {
        private TextView mTvItem;
    }
    
    /**
     * Filter for filtering list items
     */
    private class ItemFilter extends Filter {
    
        /**
         * Invoked on a background thread.  This is where all the filter logic should go
         * @param constraint the constraint to filter on
         * @return the resulting list after applying the constraint
         */
        @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<String> resultList = new ArrayList<>();
                for (String str : mData) {
                    if (str.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) {
                notifyDataSetInvalidated();
            } else {
                mFilteredData = (ArrayList<String>)results.values;
                notifyDataSetChanged();
            }
        }
    }