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.
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
...