In my last post I showed how to create your own Adapter to display items how you want to in a ListView. In this post I will show you how to extend that example to allow you to set your adapter to an AutoComplete box in android. You can’t bind just any Adapter to an AutoComplete in android. In addition to deriving from the BaseAdapter class it also has to implement the Filterable interface. I thought that this would be a piece of cake and that simply extending the interface would cause some magic to happen and I would be done. Unfortunately this wasn’t the case. The interface has one method called getFilter() which simply returns an object of type Filter.

Again simple enough except that Filter is an abstract class and in order to get this to work I had to create my own Filter class. I did some research and found that the preferred approach seems to be a nested class inside of the Adapter so I went from there. I created my class and created a constructor and the stubs of the two methods we need to override from the base class.

   1: private class GameFilter extends Filter {
   2:  
   3:     public GameFilter() {
   4:         
   5:     }
   6:     
   7:     @Override
   8:     protected FilterResults performFiltering(CharSequence constraint) {
   9:  
  10:     }
  11:  
  12:     @SuppressWarnings("unchecked")
  13:     @Override
  14:     protected void publishResults(CharSequence constraint, FilterResults results) {
  15:  
  16:     }
  17:  
  18: }

After that I had to make a minor change to our Adapter class. I needed to add another ArrayList that would be responsible for keeping track of the original data and an object of our newly created class that will be returned in the getFilter method required for the interface. All that was left was to implement the methods in our Filter class. The performFilter method was easy since we simply had to look through the list and find games whose name contained the constraint parameter and and them to a temporary list. Once we were finished, we assigned this list to the values member on the FIlterResults class we were returning. Then in the publishResults method we assigned that list to our games list, which is the list the AutoComplete is binding to and notify it of the change. This method is called asynchronously from the main thread which is why it is void. Here is the complete code for the completed adapter class.

   1: public class GameAdapter extends BaseAdapter implements Filterable {
   2:  
   3:     Context _context;
   4:     ArrayList<Game> games;
   5:     
   6:     public GameAdapter(Context context, ArrayList<Game> _games) {
   7:         _context = context;
   8:         games = _games;
   9:         orig = games;
  10:         filter = new GameFilter();
  11:     }
  12:     
  13:     @Override
  14:     public int getCount() {
  15:         if (games != null)
  16:             return games.size();
  17:         else
  18:             return 0;
  19:     }
  20:  
  21:     @Override
  22:     public Object getItem(int arg0) {
  23:         return games.get(arg0);
  24:     }
  25:  
  26:     @Override
  27:     public long getItemId(int arg0) {
  28:         return games.get(arg0).getID();
  29:     }
  30:  
  31:     @Override
  32:     public View getView(int arg0, View arg1, ViewGroup arg2) {
  33:         GameView gv;
  34:         if (arg1 == null)
  35:             gv = new GameView(_context, games.get(arg0));
  36:         else {
  37:             gv = (GameView) arg1;
  38:             gv.setID(games.get(arg0).getID());
  39:             gv.setName(games.get(arg0).getName());
  40:         }
  41:         return gv;
  42:     }
  43:  
  44:     @Override
  45:     public Filter getFilter() {
  46:         return filter;
  47:     }
  48:     
  49:     private GameFilter filter;
  50:     ArrayList<Game> orig;
  51:     
  52:     private class GameFilter extends Filter {
  53:  
  54:         public GameFilter() {
  55:             
  56:         }
  57:         
  58:         @Override
  59:         protected FilterResults performFiltering(CharSequence constraint) {
  60:             FilterResults oReturn = new FilterResults();
  61:             ArrayList<Game> results = new ArrayList<Game>();
  62:             if (orig == null)
  63:                 orig = games;
  64:             
  65:             if (constraint != null)
  66:             {
  67:                 if (orig != null && orig.size() > 0) {
  68:                     for (Game g : orig) {
  69:                         if (g.getName().contains(constraint))
  70:                             results.add(g);
  71:                     }
  72:                 }
  73:                 oReturn.values = results;
  74:             }
  75:             return oReturn;
  76:         }
  77:  
  78:         @SuppressWarnings("unchecked")
  79:         @Override
  80:         protected void publishResults(CharSequence constraint, FilterResults results) {
  81:             games = (ArrayList<Game>)results.values;
  82:             notifyDataSetChanged();
  83:         }
  84:     }
  85:  
  86: }

Here is how to set the Adapter.

   1: public class ScoreKeeper extends Activity {
   2:  
   3:     ArrayList<Game> games;
   4:     AutoCompleteTextView txtGames;
   5:     GameAdapter myAdapter;
   6:     
   7:     /** Called when the activity is first created. */
   8:     @Override
   9:     public void onCreate(Bundle savedInstanceState) {
  10:         super.onCreate(savedInstanceState);
  11:         setContentView(R.layout.main);
  12:         
  13:         txtGames = (AutoCompleteTextView)findViewById(R.id.txtGames);
  14:        
  15:         games = getAllGames();
  16:  
  17:         myAdapter = new GameAdapter(this, games);
  18:         txtGames.setAdapter(myAdapter);
  19:     }
  20: }

If you run this the AutoComplete list will now look like the list from the previous example. I had hoped to be able to use the same list for both a ListView and an AutoComplete but was unable to do so. The ListView’s items would be narrowed down as I typed into the AutoComplete box. I can’t think of any way to do this of the top of my head but it would be cool to be able to use the same adapter for two different things. I would be very interested if anyone else has done this or has any ideas on how to do it. I will definitely post a follow up if I am able to do so.