filtering custom adapter IndexOutOfBoundsException
Asked Answered
A

4

8

I'm novice at android.

My custom Adapter cause exception when filtering.

here is my code. private class DeptAdapter extends ArrayAdapter implements Filterable {

    private ArrayList<Dept> items;
    private ArrayList<Dept> mOriginalValues;

    public DeptAdapter(Context context, int textViewResourceId, ArrayList<Dept> items) {
            super(context, textViewResourceId, items);
            this.items = items;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;

        if (v == null) {
            LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.item_listview_2line, null);
        }
        Dept d = items.get(position);
        if (d != null) {
                TextView tt = (TextView) v.findViewById(R.id.toptext);
                TextView bt = (TextView) v.findViewById(R.id.bottomtext);
                if (tt != null){
                    tt.setText(d.dept_nm);                            
                }
                if(bt != null){
                    bt.setText(d.dept_cd);
                }
        }
        return v;
    }

    @Override
    public Filter getFilter() {
        Filter filter = new Filter() {

            @SuppressWarnings("unchecked")
            @Override
            protected void publishResults(CharSequence constraint,FilterResults results) {

                items = (ArrayList<Dept>) results.values; // has the filtered values
                if (results.count > 0) {
                    notifyDataSetChanged();
                } else {
                    notifyDataSetInvalidated();
                }
            }

            }
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();        // Holds the results of a filtering operation in values
                ArrayList<Dept> FilteredArrList = new ArrayList<Dept>();

                if (mOriginalValues == null) {
                    mOriginalValues = new ArrayList<Dept>(items); // saves the original data in mOriginalValues
                }

                if (constraint == null || constraint.length() == 0) {

                    // set the Original result to return  
                    results.count = mOriginalValues.size();
                    results.values = mOriginalValues;
                } else {
                    constraint = constraint.toString().toLowerCase();
                    for (int i = 0; i < mOriginalValues.size(); i++) {
                        Dept d = mOriginalValues.get(i);
                        if (d.dept_cd.toLowerCase().startsWith(constraint.toString()) || d.dept_nm.toLowerCase().startsWith(constraint.toString())) {
                            FilteredArrList.add(d);
                        }
                    }
                    // set the Filtered result to return
                    results.count = FilteredArrList.size();
                    results.values = FilteredArrList;
                }
                return results;
            }
        };
        return filter;
    }
}

class Dept
{
    String dept_cd; 
    String dept_nm; 
    public Dept(String dept_cd, String dept_nm)
    {
        this.dept_cd = dept_cd;
        this.dept_nm = dept_nm;
    }
    public String toString()
    {
        return this.dept_nm+ "(" + this.dept_cd +")" ;
    }
}

help me anyone.... I can't understand why getView() method was invoked more then items.size()

Avocation answered 4/3, 2013 at 4:59 Comment(2)
Please add complete code of DeptAdapter class and logcat outputSolleret
I also want code of the two methods getCount() and getItem(int position)Solleret
F
15

Keep in mind that getView() will query the size of the items that the superclass has, which right now, is what you originally passed it when calling the superclass constructor,

super(context, textViewResourceId, items);

Therefore, the superclass doesn't know that you've changed the size when you have filtered. This means getCount() will return the original size of the array, which is understandably larger than your filtered array.

This means You should override the getCount() method so you're sure that you're returning the actual valid size:

@Override
public int getCount()
{
   return items.size();
}

You should also override the other methods related to the List operations (such as getting) if you are going to be using them. Eg:

@Override
public Dept getItem (int pos){
     return items.get(pos);
}
Fictional answered 4/3, 2013 at 5:2 Comment(1)
Thank you very much!! Your answer was completely solves my problem.Avocation
V
1

You need to add this methods for better performance:

        @Override
        public int getCount() {
            return items.size();
        }

        @Override
        public Object getItem(int position) {
            return this.items.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }
Violante answered 29/6, 2015 at 4:27 Comment(0)
O
0

You are missing getCount() method, look at this demo

I hope it will be helpful !

Oxtail answered 4/3, 2013 at 5:5 Comment(0)
C
0
 private ArrayList<Dept> items;
    private ArrayList<Dept> mOriginalValues;

    public DeptAdapter(Context context, int textViewResourceId, ArrayList<Dept> items) {
            super(context, textViewResourceId, items);
            this.items = items;
            this.mOriginalValues=items;  //add this line in your code
    }
Claxton answered 4/3, 2013 at 5:16 Comment(4)
mOriginalValues is a placeholder while filtering is in place. It doesn't need to hold on to the items from the start.Fictional
if (mOriginalValues == null) { mOriginalValues = new ArrayList<Dept>(items); // saves the original data in mOriginalValues } you see here first use and then initialize bro @FictionalClaxton
It's initialized there if it is null, and that's where you save a copy. Assigning in the constructor is a good fail-safe, but the null check is already there when you filter for a reason. this.mOriginalValues=items; is not needed in the constructor.Fictional
Take a look at the ArrayAdapter source. Search for init(). mOriginalValues is not instantiated from the beginning. So no, instantiating this object in the constructor won't do much.Fictional

© 2022 - 2024 — McMap. All rights reserved.