Search code examples
androidlistviewandroid-listviewtextwatcherandroid-filter

How to filter listView in Basedapter before getView()?


There are lots of tutorials on how to filter listView depending on user input in EditText: initially I have my whole list which is being reduced while input.

I need to do differently: when Activity is started, there's no listView, but when user input matches any item in the list, a listView occurs. The problem is: my getView() either inflates everything available on start (without filtering) or causes NullPointerException if I refer to filtered results right on start.

The solution seems to be evident, but as far as it's my first app, I can't come up with it.

My Activity:

public class AddTask extends Activity implements View.OnClickListener {

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

    EditText addTagHere = (EditText) findViewById(R.id.addTagHere);
    addTagHere.addTextChangedListener(new TagWatcher(addTagHere));

    GridView autocompleteTagGrid = (GridView) findViewById(R.id.autocomplete_tag_grid);
    TagAdapter tagAdapter = new TagAdapter(this);
    autocompleteTagGrid.setAdapter(tagAdapter);

    autocompleteTagGrid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            my_method_here;
        }
    });
}

public class TagWatcher implements TextWatcher {

    public EditText editText;

    public TagWatcher(EditText editText) {
        super();
        this.editText = editText;
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        tagAdapter.getFilter().filter(s);
        tagAdapter.notifyDataSetChanged();
    }

    @Override
    public void afterTextChanged(Editable s) {

    }
}

My Adapter:

public class TagAdapter extends BaseAdapter implements Filterable {

private Context context;
LayoutInflater inflater;
List<MyTag> tags = getTagList();
List<MyTag> filteredTags;
TextView tagBubble;

public TagAdapter(Context context) {
    this.context = context;
    inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

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

    if (convertView == null) {
        convertView = inflater.inflate(R.layout.tag_bubble, null);
    }
    tagBubble = (TextView) convertView.findViewById(R.id.one_tag_bubble);
    tagBubble.setText(filteredTags.get(position).getMyTag());

    return convertView;
}

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

@Override
public Object getItem(int position) {
    return tags.get(position);
}

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

public List<MyTag> getTagList() {
    List<MyTag> tags = new ArrayList<>();
    SQLiteDatabase db = DBHelper.getHelper(context).getReadableDatabase();
    Cursor cursor = db.query(some query);
    if (cursor != null && cursor.moveToFirst()) {
        for (int i = 0; i < cursor.getCount(); i++) {
            MyTag oneTag = new MyTag();
            oneTag.setIdTag(cursor.getInt(cursor.getColumnIndex(DBHelper.COLUMN_ID)));
            oneTag.setMyTag(cursor.getString(cursor.getColumnIndex(DBHelper.COLUMN_TAG)));
            tags.add(oneTag);
            cursor.moveToNext();
        }
        cursor.close();
        db.close();
    }
    return tags;
}

public Filter getFilter() {
    return new InputFilter();
}

public class InputFilter extends Filter {

    @Override
    protected FilterResults performFiltering(CharSequence beginning) {

        FilterResults results = new Filter.FilterResults();

        if (beginning == null || beginning.length() < 3) {
            results.values = null;
            results.count = 0;
        } else {
            filteredTags = new ArrayList<>();

            for (MyTag p : tags) {
                if (p.getMyTag().toUpperCase().contains(beginning.toString().toUpperCase()))
                    filteredTags.add(p);
            }
            results.values = filteredTags;
            results.count = filteredTags.size();
        }
        return results;
    }

    @Override
    protected void publishResults(CharSequence beginning, FilterResults results) {

        if (results.count == 0) {
            notifyDataSetInvalidated();
        } else {
            tags = (List<MyTag>) results.values;
            notifyDataSetChanged();
        }
    }
}

Solution

  • You are using so much complicated mechanism in Adapter. Just use two arraylist , one will keep whole data. Iterate it to match the word searched. If any element contains the searched word just add it to second list, and show all the items of second list. Call notifyDataSetChanged(), in adapter class itself. Hope it helps