Search code examples
javaandroidlistviewsearchview

SearchView filter ListView


I have been attempting to to filter/search the Adapter adapter with the mSearch EditText. I am looking for help with implementing getFilter() and so on.

This activity is currently displaying a list of content. mSearch is placed above the list and I would like to filter/update the list as someone types.

Let me know if you need specific pieces of code or for more information. Thank you in advance!

public class MainActivity extends FragmentActivity {

 private SearchView mSearch;
 private List<Model> mMain = new ArrayList<Model>();

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
 }

 public class MainFragment extends Fragment implements SearchView.OnQueryTextListener {

  private ListView mListView;
  private Adapter mAdapter;

  public boolean onQueryTextChange(String query) {
   if (mAdapter != null) {
    mAdapter.getFilter().filter(query.toString());
   }
   return false;
  }

  public boolean onQueryTextSubmit(String query) {
   onQueryTextChange(query);
   return false;
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
   View rootView = inflater.inflate(R.layout.main_user_list_row, container, false);

    mListView = (ListView) findViewById(R.id.listView);
    mAdapter = new Adapter();
    mListView.setAdapter(mAdapter);
    mListView.setAdapter.setTextFilterEnabled(true);

    mSearch.setOnQueryTextListener((SearchView.OnQueryTextListener) mContext);

   return rootView;

  }

  public class Adapter extends BaseAdapter implements Filterable {

   @Override
   public int getCount() {
    return mMain.size();
   }

   @Override
   public Object getItem(int position) {
    return 1;
   }

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

   @Override
   public View getView(int position, View convertView, ViewGroup parent) {
    View view = View.inflate(mContext, R.layout.main_user_list_row, null);

     if (mContext != null && mMain.get(position) != null) {
      TextView mPosition = (TextView) view.findViewById(R.id.position);
      mPosition.setText("...");
     }

    return view;
   }

   @Override
   public Filter getFilter() {
    return new Filter() {

     @SuppressWarnings("unchecked")
     @Override
     protected void publishResults(CharSequence constraint, FilterResults results) {
      mMain = (List<Model>) results.values;
      Adapter.this.notifyDataSetChanged();
     }

     @Override
     protected FilterResults performFiltering(CharSequence constraint) {
      List<Contest> filteredResults = getFilteredResults(constraint);
      FilterResults results = new FilterResults();
      results.values = filteredResults;
      return results;
     }

    };
   }

  }

}

I am getting this error at getFilteredResults

Cannot resolve method 'getFilteredResults(java.lang.CharSequence)'

I also am getting this error at mSearch.setOnQueryTextListener((SearchView.OnQueryTextListener) mContext);

FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{...MainActivity}: java.lang.ClassCastException: ...InDraftActivity cannot be cast to android.widget.SearchView$OnQueryTextListener

java.lang.ClassCastException: ...MainActivity cannot be cast to android.widget.SearchView$OnQueryTextListener

SOLUTION

I fixed the SearchView error by implementing an OnQueryTextListener in the activity, instead of the fragment.

performFiltering needed to look like

List<Contest> filteredResults = new ArrayList<Contest>();
FilterResults results = new FilterResults();
results.values = filteredResults;
results.count = filteredResults.size();

See the marked answer.


Solution

  • Before you return your FilterResults, you must specify its value AND count. Currently you are just specifying the value like so...

    results.values = filteredResults;
    

    Under that line, before you return results in your performFiltering(...) method, add this line...

    results.count = filteredResults.size();
    

    EDIT: An fully working adapter with filter from a project i've done

    class ContactsAdapter extends BaseAdapter implements Filterable {
    
        private List<Contact> mData;
        private List<Contact> database;
        private LayoutInflater mInflater;
        private Context context;
        static boolean inMarkMode = false;
    
        ContactsAdapter(List<Contact> mData, List<Contact> database, Context context) {
            this.mData = mData;
            this.context = context;
            this.database = database;
            mInflater = LayoutInflater.from(context);
        }
    
        @Override
        public int getCount() {
            return mData.size();
        }
    
        /**
         * Modifies the way a contact is shown in its adapter depending on
         * displayChoice shared preference
         * 
         * @param position
         *            of the contact in its listview
         * @return <code>String</code> to display
         */
        @Override
        public String getItem(int position) {
            switch (context.getSharedPreferences("settings",
                    HomeScreenActivity.MODE_PRIVATE).getInt("displayChoice", -1)) {
            case 1:
                return mData.get(position).getLastName() + " "
                        + mData.get(position).getFirstName();
            case 2:
                return mData.get(position).getFirstName() + " "
                        + mData.get(position).getMobileNumber();
            case 3:
                return mData.get(position).getLastName() + " "
                        + mData.get(position).getMobileNumber();
            default:
                return mData.get(position).getFirstName() + " "
                        + mData.get(position).getLastName();
            }
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        /**
         * Modifies the view shown in HomeScreenActivity depending on whether the
         * user has entered mark mode or not
         * 
         * @return <code>View</code> to display
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
    
            CheckedTextView result = null;
            TextView normalResult = null;
            final String contactRow = getItem(position);
    
            if (inMarkMode) {// if user is in markMode, use the mark layout
                if (convertView == null) {
                    result = (CheckedTextView) mInflater.inflate(
                            R.layout.home_screen_contacts_mark_view, parent, false);
                } else {
                    result = (CheckedTextView) convertView;
                }
                result.setText(contactRow);
                result.setBackgroundResource(R.drawable.list_selector);
            } else { // if user NOT in markmode, use normal contacts view layout
                if (convertView == null) {
                    normalResult = (TextView) mInflater.inflate(
                            R.layout.home_screen_contacts_view, parent, false);
                } else {
                    normalResult = (TextView) convertView;
                }
                normalResult.setText(contactRow);
                normalResult.setBackgroundResource(R.drawable.list_selector);
            }
    
            if (inMarkMode) {
                return result;
            } else {
                return normalResult;
            }
        }
    
        int getItemIdAtPosition(int position) {
            return mData.get(position).getID();
        }
    
        @Override
        public Filter getFilter() {
            return new Filter() {
                @Override
                protected FilterResults performFiltering(CharSequence charSequence) {
                    FilterResults results = new FilterResults();
                    // If there's nothing to filter on, return the original data for
                    // your list
                    if (charSequence == null || charSequence.length() == 0) {
                        results.values = database;
                        results.count = database.size();
                    } else {
                        List<Contact> filterResultsData = new ArrayList<Contact>();
    
                        // if search details is 0, search fullName, else, search
                        // all details
                        if (HomeScreenActivity.searchAllDetails == 0) {
                            for (Contact c : database) {
                                if (c.getFullName().toLowerCase(Locale.ENGLISH)
                                        .contains(charSequence)) {
                                    filterResultsData.add(c);
                                }
                            }
                        } else {
                            for (Contact c : database) {
                                if (c.getAllDetailsForSearch()
                                        .toLowerCase(Locale.ENGLISH)
                                        .contains(charSequence)) {
                                    filterResultsData.add(c);
                                }
                            }
                        }
    
                        results.values = filterResultsData;
                        results.count = filterResultsData.size();
                    }
    
                    return results;
                }
    
                @SuppressWarnings("unchecked")
                @Override
                protected void publishResults(CharSequence charSequence,
                        FilterResults filterResults) {
                    // set the data to the filter results and notifyDataSetChanged()
                    mData = (List<Contact>) filterResults.values;
                    notifyDataSetChanged();
                }
            };
        }
    }