3

In this Stackoverflow answer, it is indicated that filtering can be accomplished in a ListView without overriding the getFilter method of the ArrayAdapter and instead implementing toString in the POJO class.

I have tried implementing it, but the filtering is not working correctly. Although the ListView does filter, it doesn't show the correct items in the array. So, for example, if the filter matches a single row in the array then one item is shown in the ListView, but it is the wrong item that is shown. In this scenario, the first item of the array is always shown and not the item that actually matches the entered search text.

Here is the code for my ArrayAdapter:

public class TitleListingArrayAdapter extends ArrayAdapter<Title> {

    private List<Title> items;
    private Context context;

    public TitleListingArrayAdapter(Context context, int textViewResourceId, List<Title> items) {
        super(context, textViewResourceId, items);
        this.items = items;
        this.context = context;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
    {
        View view = convertView;
        if (view == null) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.titlelisting_single_row, null);
        }
        Title item = items.get(position);
        if (item!= null) {
            TextView titleView = (TextView) view.findViewById(R.id.title);            
            if (titleView != null) {
                titleView.setText(item.getName());
            }
            TextView yearView = (TextView) view.findViewById(R.id.year);
            if (yearView != null) {
                yearView.setText(String.valueOf(item.getYear())+", ");
            }
            TextView genreView = (TextView) view.findViewById(R.id.genre);
            if (genreView != null) {
                genreView.setText(item.getGenre());
            }
            TextView authorView = (TextView) view.findViewById(R.id.author);
            if (authorView != null) {
                authorView.setText(item.getAuthor());
            }
            RatingBar ratingView = (RatingBar) view.findViewById(R.id.rating);
            if (ratingView != null) {
                ratingView.setRating(item.getRating());
            }
            ImageView iconView = (ImageView) view.findViewById(R.id.list_image);
            iconView.setImageResource(lookupResourceId(context, item.getID()));            
        }
        return view;
    }

    private int lookupResourceId(Context context, String id) {
        String resourceName =  "thumb_"+id;
        return context.getResources().getIdentifier(resourceName, "drawable", context.getPackageName());
    }
}

Here is the relevant section of my Activity code:

 @Override
 public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.listing);
    databaseHandler = new DatabaseHandler(this);
    listView = (ListView) findViewById(R.id.list);
    List<Title> titles = databaseHandler.getAllTitles();
    adapter = new TitleListingArrayAdapter(this, R.id.list, titles);
    listView.setAdapter(adapter);
    filterText = (EditText) findViewById(R.id.filter);
    filterText.addTextChangedListener(filterTextWatcher);
}

private TextWatcher filterTextWatcher = new TextWatcher() {
    public void afterTextChanged(Editable s) {}
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        adapter.getFilter().filter(s.toString().toLowerCase());
    }
};

The Title POJO class implements toString as follows:

@Override
public String toString() {
    String name = this.getName() == null ? "" : this.getName().toLowerCase();
    String year = this.getYear() == null ? "" : this.getYear().toString();
    String genre = this.getGenre() == null ? "" : this.getGenre().toLowerCase();
    return name + " " +year+ " "+ genre;
}

Does anyone have any idea why the filtering is not working correctly and how I could fix it?

3
  • Could you provide a few examples of what you expect and what you actually get? Commented Feb 23, 2013 at 16:47
  • Looking over your code: you should implement ViewHolders (watch Google's Turbo-charge Your UI), toLowerCase() is redundant so you can remove that, and I would recommend sticking with Cursors, CursorAdapters, & FilterQueryProviders while working with databases. They are much faster than converting everything to Lists. Commented Feb 23, 2013 at 16:59
  • Thanks, Sam, for the information. I have now implemented ViewHolders in my ArrayAdapter and am looking at replacing the 'ArrayAdapter` with a CursorAdapter. I used ArrayAdapter because I am new at Android development and the ListView example that I found and based my code on made use of an ArrayAdapter. I will research Cursors and CursorAdapters. Thanks. Commented Feb 23, 2013 at 21:45

1 Answer 1

6

The following question deals with exactly the same issue that I encountered. This question also gives an example of what the filtering is doing, showing the correct number of items by not showing the correct items in the list.

So it seems that this answer is wrong, despite being upvoted six times. I resolved this by not making use of the getFilter method of the ArrayAdapter at all. Rather, I create a new ArrayAdapter in my TextWatcher instance, as follows:

private TextWatcher filterTextWatcher = new TextWatcher() {
     public void afterTextChanged(Editable s) {}
     public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
     public void onTextChanged(CharSequence s, int start, int before, int count) {
         if (!s.toString().equals("")) {
              List<Title> filteredTitles = new ArrayList<Title>();
              for (int i=0; i<titles.size(); i++) {
                   if (titles.get(i).toString().contains(s)) {
                       filteredTitles.add(titles.get(i));                   
                   }            
              }
              adapter = new TitleListingArrayAdapter(TitleListingActivity.this, R.id.list, filteredTitles);
              listView.setAdapter(adapter);
         }
         else {
              adapter = new TitleListingArrayAdapter(TitleListingActivity.this, R.id.list, titles);
              listView.setAdapter(adapter);             
         }
    }
};

Note that I also moved the declaration List<Title> titles out of onCreate and made it a member variable of my Activity class so that it is accessible inside the onTextChanged method of filterTextWatcher.

Sign up to request clarification or add additional context in comments.

2 Comments

The R.id.list is derived from where? I am having an issue with my Adapter: stackoverflow.com/questions/20524417/…
This is it mate! This is it! This is what i was looking for and now i can hopefully complete my technical assignment for an interview. Thanks a lot mate!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.