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;
}
}
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()
.
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 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
.