In my android application I would like to display items in a list. For this I’m using listView. Because the list items consist of a text (name) and an editText (quantity) I’m using custom ArraryAdapter. I’m using editText because I would like to allow the user to modify the quantity. When the user has stopped editing, I would like to store the new value, so I have added an onTextChanged event to the editTexts.
I created a class for the items (that I’m displaying in the list). It’s very simple, it has only name and quantity:
public class Foo {
final String name;
int quantity;
public Foo (String name, int quantity) {
this.name = name;
this.quantity = quantity;
}
// for debug message
@Override
public String toString () {
return "name: " + this.name+ " quantity: " + this.quantity;
}
}
In the main activity’s onCreate method I create a list of Foo elements and I use an own Adapter for them to put in the list:
public class MainActivity extends Activity {
ListView list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list = (ListView) findViewById (R.id.list);
List<Foo> elements = new ArrayList<Foo> ();
elements.add(new Foo ("foo", 1));
elements.add(new Foo ("bar", 2));
elements.add(new Foo ("baz", 3));
elements.add(new Foo ("foo2", 4));
elements.add(new Foo ("bar2", 5));
elements.add(new Foo ("baz2", 6));
FooAdapter adapter = new FooAdapter (this, R.layout.foo_elements, elements);
//where foo_elements contains a horizontal LinearLayout and in it
//there's a TextView and an EditText
list.setAdapter(adapter);
}
So my adapter is the following (I found the holder pattern here: http://www.vogella.com/articles/AndroidListView/article.html#adapterperformance_hoder):
public class FooAdapter extends ArrayAdapter<Foo> {
Context context;
int layoutResourceId;
List<Foo> elements;
public FooAdapter (Context context, int layoutResourceId, List<Foo> elements) {
super (context, layoutResourceId, elements);
this.context = context;
this.layoutResourceId = layoutResourceId;
this.elements = elements;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
Holder holder = null;
final Foo item = elements.get(position);
if (row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater ();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new Holder ();
holder.name = (TextView) row.findViewById (R.id.name);
holder.quantity = (EditText) row.findViewById(R.id.quantity);
holder.quantity.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
@Override
public void afterTextChanged(Editable s) {
Log.d("textchanged", "text has changed");
Log.d ("new text", s.toString());
item.quantity = Integer.parseInt(s.toString());
}
});
row.setTag(holder);
}
else {
holder = (Holder) row.getTag();
}
holder.name.setText (item.name);
holder.quantity.setText (String.valueOf(item.quantity));
return row;
}
class Holder {
TextView name;
EditText quantity;
}
}
As you can see I used TextWatcher for listening to the textchanged event and in afterTextChanged function I would like to set the new quantity to the Foo object.
I put some debug messages to see what’s happening there and I added a button to print the Foo objects. After starting my application the output is this:
textchanged text has changed
new text 1
textchanged text has changed
new text 2
textchanged text has changed
new text 3
textchanged text has changed
new text 4
textchanged text has changed
new text 5
textchanged text has changed
new text 6
textchanged text has changed
new text 6
textchanged text has changed
new text 2
textchanged text has changed
new text 3
textchanged text has changed
new text 4
textchanged text has changed
new text 5
textchanged text has changed
new text 6
So after launching the program the quantities are:
name: foo quantity: 6 //instead of name: foo quantity: 1
name: bar quantity: 2
name: baz quantity: 3
name: foo2 quantity: 4
name: bar2 quantity: 5
name: baz2 quantity: 6
If I modify the last element then it changes the first element too:
name: foo quantity: 62 //instead of name: foo quantity: 1
name: bar quantity: 2
name: baz quantity: 3
name: foo2 quantity: 4
name: bar2 quantity: 5
name: baz2 quantity: 62
I’m new to Android (even Java) and I didn’t find my fault. Do you have any idea? And why so many textchanged events? How does it work? Do I use the listView wrongly? Thanks in advance.
I think your problem it here.
item variable can be not that you want there. See this post: Android SimpleAdapter wrong data-item gets associated with a list row it describes that getView can be called not one time and view can be reused. You successfully impliment it with setting name and value here:
But not with EditText. Set tag to your edit view, or retrieve item in an other way to correct it.
Hope it will help.