RowAdapter deletes random rows that are added dynamically to ListView?

Go To StackoverFlow.com

0

I am attempting to dynamically add and remove rows elements from a ListView.

Each element in the ListView is a LinearLayout. I can add and remove rows, but whenever I do either the order of the rows is jumbled and the correct row is not necessarily removed. I have checked that my ArrayList<LinearLayout> rows is increasing in size every time I add a row, but a call to getItem(position) is always null. I think this is causing the issue, but why are null layouts being added to rows?

Edit: I changed the ArrayAdapter parameter from LinearList to Row, which is a simple object that contains getters and setters for two EditText fields. This has fixed the null problem, but the same issue is still there - random rows are deleted. In getView() below, Logcat shows that position = 0 in every case of my delete button's onClick(). Logcat also shows that each View in the ListView is getting randomly assigned a position.

My new Adapter code:

public class RowAdapter extends ArrayAdapter<Row>{

    private ArrayList<Row> rows;

    public RowAdapter(Context context, int textViewResourceId, ArrayList<Row> rows) {
        super(context, textViewResourceId, rows);
        this.rows = rows;
    }

    public Row getItem(int position){
        return rows.get(position);
    }

    public int getCount(){
        return rows.size();
    }

    public static class ViewHolder {
        public EditText name;
        public EditText value;
        public Button delete; 
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;
        ViewHolder viewHolder;

        if(v == null){
            LayoutInflater li = (LayoutInflater)getContext().
                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = li.inflate(R.layout.row, parent, false);

            viewHolder = new ViewHolder();

            viewHolder.name = (EditText)v.findViewById(R.id.editAttrName);
            viewHolder.value = (EditText)v.findViewById(R.id.editAttrValue);

            viewHolder.delete = (Button)v.findViewById(R.id.btnDelete);
            viewHolder.delete.setTag(position);
            viewHolder.delete.setOnClickListener(new AdapterView.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = (Integer) v.getTag();
                    Log.e("RowAdapter", "position = " + position);
                    Row row = getItem(position);
                    rows.remove(row);
                    notifyDataSetChanged();
                }   
            });

            v.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder)v.getTag();
        }
        Log.e("RowAdapter", "row " + v.toString() + ", position = " + position);    
        Row row = getItem(position);

        if(row != null) {

            EditText editText1 = (EditText)v.findViewById(R.id.editAttrName);
            EditText editText2 = (EditText)v.findViewById(R.id.editAttrValue);

            String text1 = editText1.getText().toString();
            String text2 = editText2.getText().toString();
            if(viewHolder.name != null) {
                viewHolder.name.setText(text1);
            }
            if(viewHolder.value != null) {
                viewHolder.value.setText(text2);
            }

        }

        return v;
    }
}
2012-04-03 22:17
by A D


1

Your problem is probably that Adapters are meant to contain a list of models , not a list of views. The views should be generated by your getView() method. So what you really want is to create a Row class that represents a row, then you can inflate and populate the views inside the getView().

2012-04-03 23:56
by dmon
Thanks for the response! I'm going to give that a whirl and will let you know how it goes - A D 2012-04-04 00:03
I have implemented the changes you suggested, but I am still getting a similar problem. I will update the question to reflect these changes - A D 2012-04-04 06:37
Hmmmm... I took a quick look at your code and you're not using the new Row object anywhere. However, you say that it "contains getters and setters for two EditText fields", which is wrong. Again, you shouldn't store the actual EditText, you need to store the plain string values. See this for a good example - dmon 2012-04-04 14:06
I'm using the new row after inflating the view (if applicable). I should have been more clear, the Row object does store plain string texts. I attempt to get any String that the user has typed into the view and then I store it in the Row object. I am going to take a look at that example. Thanks for your help thus far - A D 2012-04-04 15:36


1

I don't think you need to create a function public Row getItem(int position) as ArrayAdapter<T> provides a built in function called getItemAtPosition(int position). Also make sure when you are calling getItemAtPosition(int position) the active view is the list view or you have handle to your list view so that you can do view.getItemAtPosition(position), otherwise you'll get null.

2012-04-04 06:51
by Mayank
Thanks for your reply. I'm not sure if that is what I want, because doesn't getItemAtPosition() return a View (in this case, the ListView contains LinearLayouts)? I think that I need to update the ArrayList and then notify the adapter that my data set has change - A D 2012-04-04 15:37
Ads