androidlistviewandroid-arrayadapterandroid-filterable

ListView using a custom Adapter to implement a Filter is getting IndexOutOfBoundsException


I have a custom ListView. It works fine except when attempting to filter a user search.

The code for displaying the ListView:

private void listShow() {
    warranties=db.getAllServWarr();
    adapter=new WarrantyAdapter(serviceswarranty_activity.this,warranties);
    listView.setAdapter(adapter);
}

The code that implements the filter in the custom adapter:

public class WarrantyAdapter extends ArrayAdapter implements Filterable{
private ArrayList<ServicesWarranty> warrantyList;
private ArrayList<ServicesWarranty> filteredList;

private ItemFilter mFilter = new ItemFilter();
private Context context;
private int currPosition;
public WarrantyAdapter(@NonNull Context context,ArrayList<ServicesWarranty> warrantyList) {
    super(context, R.layout.serviceswarranty_item,warrantyList);
    this.warrantyList=warrantyList;
    this.filteredList=warrantyList;
    this.context=context;
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    ServicesWarranty sw=warrantyList.get(position);
    currPosition=position;
    WarrantyAdapter.ViewHolder holder;
    if(convertView==null){
        LayoutInflater inflater=(LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView=inflater.inflate(R.layout.serviceswarranty_item,parent,false);
        holder=new WarrantyAdapter.ViewHolder();
        holder.lockno=(TextView)convertView.findViewById(R.id.lockNum_warrantyitem);
        holder.fromdate=(TextView)convertView.findViewById(R.id.fromDate_warrantyItem);
        holder.todate=(TextView)convertView.findViewById(R.id.toDate_warrantyItem);
        holder.editIc=(ImageView)convertView.findViewById(R.id.editic_warrantyItem);
        holder.deleteIc=(ImageView)convertView.findViewById(R.id.deleteic_warrantyItem);
        convertView.setTag(holder);
    }else{
        holder=(WarrantyAdapter.ViewHolder) convertView.getTag();
    }
    holder.fill(sw);
    return convertView;
}
private class ViewHolder{
    public TextView lockno;
    public TextView fromdate;
    public TextView todate;
    public ImageView editIc;
    public ImageView deleteIc;
    public void fill(final ServicesWarranty sw){
        lockno.setText(String.valueOf(sw.getLockNumber()));
        fromdate.setText(sw.getBeginDate());
        todate.setText(sw.getEndDate());
        editIc.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent(context,Add2warranty.class);
                intent.putExtra("AddedWarrantyLockNo",sw.getLockNumber().toString());
                context.startActivity(intent);
            }
        });
        deleteIc.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AlertDialog.Builder deleteAlert=new AlertDialog.Builder(context);
                deleteAlert.setMessage("آیا از حذف گارانتی با شماره قفل "+sw.getLockNumber().toString()+" اطمینان دارید؟")
                        .setPositiveButton("بله", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
                                AppDBHelper db=new AppDBHelper(context);
                                boolean res=db.deleteFromWarranty(sw.getID());
                                if(res) {
                                    Toast.makeText(context, "Delete successfully", Toast.LENGTH_SHORT).show();
                                    warrantyList.remove(currPosition);
                                    notifyDataSetChanged();
                                }

                            }
                        })
                        .setNegativeButton("خیر",null)
                        .show();
            }
        });
    }
}
public Filter getFilter() {
    return mFilter;
}

private class ItemFilter extends Filter {
    @Override
    protected FilterResults performFiltering(CharSequence constraint) {

        String filterString = constraint.toString();

        FilterResults results = new FilterResults();

        final List<ServicesWarranty> list = filteredList;

        int count = list.size();
        final ArrayList<ServicesWarranty> nlist = new ArrayList<ServicesWarranty>(count);

        String filterableString ;

        for (int i = 0; i < count; i++) {
            filterableString = list.get(i).getLockNumber().toString();
            if (filterableString.toLowerCase().contains(filterString)) {
                nlist.add(list.get(i));
            }
        }

        results.values = nlist;
        results.count = nlist.size();

        return results;
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void publishResults(CharSequence constraint, FilterResults results) {
        warrantyList = (ArrayList<ServicesWarranty>) results.values;
        notifyDataSetChanged();
    }
}
}

and also i add this to my activity to enable search by filter

inputSearch.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String s) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String s) {
            if(s.length()>0)
                adapter.getFilter().filter(s);
            return false;
        }
    });

When I debug this program, the adapter.getFilter().filter(s); works correctly, but near the end of function the program force closes.

Here is my logcat info

FATAL EXCEPTION: main                                                                  
Process: com.example.zahra.prj1, PID: 18592                                                                        
java.lang.IndexOutOfBoundsException: Invalid index 1, size is 1                                                                            
at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)                                                                            
at java.util.ArrayList.get(ArrayList.java:308)

In my ListView, I have 2 items with values 13 and 14. I type 4 in search text box so the list size should be one.

Why is the filtering not working? Thanks for any help.


Solution

  • The problem is the array is out of bounds because getCount() is returning the size of the warrantyList instead of the filteredList.

    There is a good example of how to handle the two lists here: Implementing Filterable

    But in general, the WarrantyAdapter should be using the filteredList. The ItemFilter should be using the warrantyList to publish the filteredList.

    I have your code running in my environment with the following changes:

    @Override
    public int getCount() {
        return filteredList.size();
    }
    
    @Override
    public Object getItem(int position) {
       return filteredList.get(position);
    }
    
    @Override
    public long getItemId(int position) {
        return position;
    }
    

    and I modified WarrantyAdapter to extend BaseAdapter and inside getView I use the filteredList...