title: Custom filtering in Android using ArrayAdapter
body: <p>I'm trying to filter my ListView which is populated with this ArrayAdapter:</p>
  
  <pre><code>package me.alxandr.android.mymir.adapters;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.Set;
  
  import me.alxandr.android.mymir.R;
  import me.alxandr.android.mymir.model.Manga;
  import android.content.Context;
  import android.util.Log;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.ArrayAdapter;
  import android.widget.Filter;
  import android.widget.SectionIndexer;
  import android.widget.TextView;
  
  public class MangaListAdapter extends ArrayAdapter&lt;Manga&gt; implements SectionIndexer
  {
      public ArrayList&lt;Manga&gt; items;
      public ArrayList&lt;Manga&gt; filtered;
      private Context context;
      private HashMap&lt;String, Integer&gt; alphaIndexer;
      private String[] sections = new String[0];
      private Filter filter;
      private boolean enableSections;
  
      public MangaListAdapter(Context context, int textViewResourceId, ArrayList&lt;Manga&gt; items, boolean enableSections)
      {
          super(context, textViewResourceId, items);
          this.filtered = items;
          this.items = filtered;
          this.context = context;
          this.filter = new MangaNameFilter();
          this.enableSections = enableSections;
  
          if(enableSections)
          {
              alphaIndexer = new HashMap&lt;String, Integer&gt;();
              for(int i = items.size() - 1; i &gt;= 0; i--)
              {
                  Manga element = items.get(i);
                  String firstChar = element.getName().substring(0, 1).toUpperCase();
                  if(firstChar.charAt(0) &gt; 'Z' || firstChar.charAt(0) &lt; 'A')
                      firstChar = "@";
  
                  alphaIndexer.put(firstChar, i);
              }
  
              Set&lt;String&gt; keys = alphaIndexer.keySet();
              Iterator&lt;String&gt; it = keys.iterator();
              ArrayList&lt;String&gt; keyList = new ArrayList&lt;String&gt;();
              while(it.hasNext())
                  keyList.add(it.next());
  
              Collections.sort(keyList);
              sections = new String[keyList.size()];
              keyList.toArray(sections);
          }
      }
  
      @Override
      public View getView(int position, View convertView, ViewGroup parent)
      {
          View v = convertView;
          if(v == null)
          {
              LayoutInflater vi = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
              v = vi.inflate(R.layout.mangarow, null);
          }
  
          Manga o = items.get(position);
          if(o != null)
          {
              TextView tt = (TextView) v.findViewById(R.id.MangaRow_MangaName);
              TextView bt = (TextView) v.findViewById(R.id.MangaRow_MangaExtra);
              if(tt != null)
                  tt.setText(o.getName());
              if(bt != null)
                  bt.setText(o.getLastUpdated() + " - " + o.getLatestChapter());
  
              if(enableSections &amp;&amp; getSectionForPosition(position) != getSectionForPosition(position + 1))
              {
                  TextView h = (TextView) v.findViewById(R.id.MangaRow_Header);
                  h.setText(sections[getSectionForPosition(position)]);
                  h.setVisibility(View.VISIBLE);
              }
              else
              {
                  TextView h = (TextView) v.findViewById(R.id.MangaRow_Header);
                  h.setVisibility(View.GONE);
              }
          }
  
          return v;
      }
  
      @Override
      public void notifyDataSetInvalidated()
      {
          if(enableSections)
          {
              for (int i = items.size() - 1; i &gt;= 0; i--)
              {
                  Manga element = items.get(i);
                  String firstChar = element.getName().substring(0, 1).toUpperCase();
                  if(firstChar.charAt(0) &gt; 'Z' || firstChar.charAt(0) &lt; 'A')
                      firstChar = "@";
                  alphaIndexer.put(firstChar, i);
              }
  
              Set&lt;String&gt; keys = alphaIndexer.keySet();
              Iterator&lt;String&gt; it = keys.iterator();
              ArrayList&lt;String&gt; keyList = new ArrayList&lt;String&gt;();
              while (it.hasNext())
              {
                  keyList.add(it.next());
              }
  
              Collections.sort(keyList);
              sections = new String[keyList.size()];
              keyList.toArray(sections);
  
              super.notifyDataSetInvalidated();
          }
      }
  
      public int getPositionForSection(int section)
      {
          if(!enableSections) return 0;
          String letter = sections[section];
  
          return alphaIndexer.get(letter);
      }
  
      public int getSectionForPosition(int position)
      {
          if(!enableSections) return 0;
          int prevIndex = 0;
          for(int i = 0; i &lt; sections.length; i++)
          {
              if(getPositionForSection(i) &gt; position &amp;&amp; prevIndex &lt;= position)
              {
                  prevIndex = i;
                  break;
              }
              prevIndex = i;
          }
          return prevIndex;
      }
  
      public Object[] getSections()
      {
          return sections;
      }
  
      @Override
      public Filter getFilter()
      {
          if(filter == null)
              filter = new MangaNameFilter();
          return filter;
      }
  
      private class MangaNameFilter extends Filter
      {
  
          @Override
          protected FilterResults performFiltering(CharSequence constraint) {
              // NOTE: this function is *always* called from a background thread, and
              // not the UI thread.
              constraint = constraint.toString().toLowerCase();
              FilterResults result = new FilterResults();
              if(constraint != null &amp;&amp; constraint.toString().length() &gt; 0)
              {
                  ArrayList&lt;Manga&gt; filt = new ArrayList&lt;Manga&gt;();
                  ArrayList&lt;Manga&gt; lItems = new ArrayList&lt;Manga&gt;();
                  synchronized (items)
                  {
                      Collections.copy(lItems, items);
                  }
                  for(int i = 0, l = lItems.size(); i &lt; l; i++)
                  {
                      Manga m = lItems.get(i);
                      if(m.getName().toLowerCase().contains(constraint))
                          filt.add(m);
                  }
                  result.count = filt.size();
                  result.values = filt;
              }
              else
              {
                  synchronized(items)
                  {
                      result.values = items;
                      result.count = items.size();
                  }
              }
              return result;
          }
  
          @SuppressWarnings("unchecked")
          @Override
          protected void publishResults(CharSequence constraint, FilterResults results) {
              // NOTE: this function is *always* called from the UI thread.
              filtered = (ArrayList&lt;Manga&gt;)results.values;
              notifyDataSetChanged();
          }
  
      }
  }
  </code></pre>
  
  <p>However, when I call filter('test') on the filter nothing happens at all (or the background-thread is run, but the list isn't filtered as far as the user conserns). How can I fix this?</p>