title: Android custom list view filter
body: <p>I know there are tons of tutorials and this has been asked before but after following a few tutorials this one in particular (<a href="http://www.survivingwithandroid.com/2012/10/android-listview-custom-filter-and.html" rel="nofollow">tutorial</a>) I keep getting a force close. The list filters sorta but scrolling causes a force close and it puts the results at the top but the length of the list stops so as soon as I pass my results I get an out of bounds error because the array size is smaller after filtering. My code is below any help is greatly appreciated!</p>
  
  <pre><code>    public class Holder {
          public boolean checked;
          public String name;
          public String type;
          public String number;
  
          public Holder(boolean checked, String name, String type, String number) {
              this.checked=checked;
              this.name=name;
              this.type=type;
              this.number=number;
          }
      }
  
  public class MyCustomAdapter extends ArrayAdapter&lt;Holder&gt; implements Filterable {
      ArrayList&lt;Holder&gt; array;
      LayoutInflater inflater2;
      HolderFilter holderFilter;
      public MyCustomAdapter(LayoutInflater inflater, Context context, int resource, int textViewResourceId, ArrayList&lt;Holder&gt; litem) {
          super(context, resource, textViewResourceId, litem);
          inflater2 = inflater;
          array = litem;
      }
  
      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
          ViewHolder holder;
          if (convertView == null) { 
              convertView = inflater2.inflate(R.layout.contactrow, null);
              holder = new ViewHolder();
              holder.checkBox = (CheckBox)convertView.findViewById(R.id.checkBox);
              holder.name = (TextView)convertView.findViewById(R.id.recipient_name);
              holder.type = (TextView)convertView.findViewById(R.id.recipient_type);
              holder.number = (TextView)convertView.findViewById(R.id.recipient_number);
              convertView.setTag(holder);
          }else{
              holder = (ViewHolder) convertView.getTag();
          }
          holder.checkBox.setOnClickListener(checkChange);
          holder.checkBox.setChecked(holderarray.get(position).checked);
          holder.checkBox.setTag(position);
          holder.name.setText(holderarray.get(position).name);
          holder.type.setText(types[Integer.valueOf(holderarray.get(position).type)]);
          holder.number.setText(holderarray.get(position).number);
          return convertView;
      }
  
      @Override
      public Filter getFilter() {
          if (holderFilter == null){
              holderFilter = new HolderFilter();
          }
          return holderFilter;
      }
  
      private class HolderFilter extends Filter {
          @Override
          protected FilterResults performFiltering(CharSequence constraint) {
              FilterResults results = new FilterResults();
              if (constraint == null || constraint.length() == 0) {
                  results.values = holderarray;
                  results.count = holderarray.size();
              }else {
                  List&lt;Holder&gt; nHolderList = new ArrayList&lt;Holder&gt;();
                  for (Holder h : holderarray) {
                      if (h.name.toUpperCase().startsWith(constraint.toString().toUpperCase()) || h.number.startsWith(constraint.toString()))
                          nHolderList.add(h);
                  }
                  results.values = nHolderList;
                  results.count = nHolderList.size();
              }
              return results;
          }
          @SuppressWarnings("unchecked")
          @Override
          protected void publishResults(CharSequence constraint,FilterResults results) {
              if (results.count == 0)
                  notifyDataSetInvalidated();
              else {
                  holderarray = (ArrayList&lt;Holder&gt;) results.values;
                  notifyDataSetChanged();
              }
          }
      }
  }
  
  static class ViewHolder {
      CheckBox checkBox;
      TextView name;
      TextView type;
      TextView number;
  }
  </code></pre>
  
  <p>I then do the following on my edit text in order to search</p>
  
  <pre><code>    final EditText search = (EditText)findViewById(R.id.search);
      search.addTextChangedListener(new TextWatcher(){
          @Override
          public void afterTextChanged(Editable arg0) {
  
          }
          @Override
          public void beforeTextChanged(CharSequence s, int start, int count, int after) {
  
          }
          @Override
          public void onTextChanged(CharSequence s, int start, int before, int count) {
              aadapter.getFilter().filter(s.toString());
          }
      });
  </code></pre>
  
  <p>Output from logcat</p>
  
  <pre><code>E/EmbeddedLogger(  509): App crashed! Process: com.test.app
  E/EmbeddedLogger(  509): App crashed! Package: com.test.app v1 (1.0)
  E/AndroidRuntime(29973): FATAL EXCEPTION: main
  E/AndroidRuntime(29973): java.lang.IndexOutOfBoundsException: Invalid index 4, size is 4
  E/AndroidRuntime(29973):    at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
  E/AndroidRuntime(29973):    at java.util.ArrayList.get(ArrayList.java:304)
  E/AndroidRuntime(29973):    at com.test.app.sms.ContactPicker$MyCustomAdapter.getView(ContactPicker.java:197)
  E/AndroidRuntime(29973):    at android.widget.AbsListView.obtainView(AbsListView.java:2350)
  E/AndroidRuntime(29973):    at android.widget.ListView.makeAndAddView(ListView.java:1937)
  E/AndroidRuntime(29973):    at android.widget.ListView.fillDown(ListView.java:789)
  E/AndroidRuntime(29973):    at android.widget.ListView.fillGap(ListView.java:753)
  E/AndroidRuntime(29973):    at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5259)
  E/AndroidRuntime(29973):    at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3323)
  E/AndroidRuntime(29973):    at android.widget.AbsListView.onTouchEvent(AbsListView.java:3609)
  E/AndroidRuntime(29973):    at android.widget.ListView.onTouchEvent(ListView.java:4027)
  E/AndroidRuntime(29973):    at android.view.View.dispatchTouchEvent(View.java:7175)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2181)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1916)
  E/AndroidRuntime(29973):    at android.widget.AbsListView.dispatchTouchEvent(AbsListView.java:6882)
  E/AndroidRuntime(29973):    at android.widget.ListView.dispatchTouchEvent(ListView.java:4441)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2187)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1930)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2187)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1930)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2187)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1930)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2187)
  E/AndroidRuntime(29973):    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1930)
  E/AndroidRuntime(29973):    at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2041)
  E/AndroidRuntime(29973):    at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1495)
  E/AndroidRuntime(29973):    at android.app.Activity.dispatchTouchEvent(Activity.java:2454)
  E/AndroidRuntime(29973):    at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1989)
  E/AndroidRuntime(29973):    at android.view.View.dispatchPointerEvent(View.java:7369)
  E/AndroidRuntime(29973):    at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3549)
  E/AndroidRuntime(29973):    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3478)
  E/AndroidRuntime(29973):    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4587)
  E/AndroidRuntime(29973):    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4566)
  E/AndroidRuntime(29973):    at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4658)
  E/AndroidRuntime(29973):    at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:184)
  E/AndroidRuntime(29973):    at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
  E/AndroidRuntime(29973):    at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:171)
  E/AndroidRuntime(29973):    at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:4637)
  E/AndroidRuntime(29973):    at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:4686)
  E/AndroidRuntime(29973):    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
  E/AndroidRuntime(29973):    at android.view.Choreographer.doCallbacks(Choreographer.java:555)
  E/AndroidRuntime(29973):    at android.view.Choreographer.doFrame(Choreographer.java:523)
  E/AndroidRuntime(29973):    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
  E/AndroidRuntime(29973):    at android.os.Handler.handleCallback(Handler.java:615)
  E/AndroidRuntime(29973):    at android.os.Handler.dispatchMessage(Handler.java:92)
  E/AndroidRuntime(29973):    at android.os.Looper.loop(Looper.java:155)
  E/AndroidRuntime(29973):    at android.app.ActivityThread.main(ActivityThread.java:5485)
  E/AndroidRuntime(29973):    at java.lang.reflect.Method.invokeNative(Native Method)
  E/AndroidRuntime(29973):    at java.lang.reflect.Method.invoke(Method.java:511)
  E/AndroidRuntime(29973):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
  E/AndroidRuntime(29973):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:795)
  E/AndroidRuntime(29973):    at dalvik.system.NativeStart.main(Native Method)
  E/EmbeddedLogger(  509): Application Label: Test App
  W/ActivityManager(  509):   Force finishing activity com.test.app/.sms.ContactPicker
  </code></pre>