I have a custom arrayadapter and I want to add an onclicklistener for a button in each one of its rows, when I click on the button I want the image resource to change, everything works fine except that when I click on a button the image changes but the image of another button in an other row also changes. Thanks for your help ! Here is my code:
public class Coursadapter extends ArrayAdapter<String>{
Context context;
int layoutResourceId;
ArrayList<String> data = null;
WeatherHolder holder;
public Coursadapter(Context context, int layoutResourceId, ArrayList<String> data) {
// super(context, layoutResourceId, data, coeff);
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row == null)
{
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new WeatherHolder();
holder.name = (TextView)row.findViewById(R.id.item_cours_name);
holder.b=(ImageButton) row.findViewById(R.id.button);
holder.b.setTag(holder);
row.setTag(holder);
}
else
{
holder = (WeatherHolder)row.getTag();
}
holder.b.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
WeatherHolder w = (WeatherHolder) v.getTag();
w.b.setImageResource(R.drawable.butgreen);
}
});
String name1 = data.get(position);
holder.name.setText(name1);
return row;
}
static class WeatherHolder
{
TextView name;
ImageButton b;
}
}
There's a common misunderstanding that your question and both answers share.
First of all, make sure you understand how ListView's recycling works. For example, this post explains this matter a bit.
Regarding this specific case, let's first understand the problem. When you click on a button, the image is changed and everything looks OK. But if you scroll, you will get the same view (with button that has a changed image) in another position. Because of recycling.
To fix this problem, you have to maintain your button's image selection. This won't magically happen. You have to add some logic that tells this method which image resource should be displayed in a particular position.
For example, in your getView() method, add the following (pseudo)code:
Few changes done check out the code
package com.example.dontpanic;
put your click listener inside if condition and set tag outside of the if else condition
The reason why this happens is because the adapter recycles the views on scrolling.
For example, suppose you have a total of 15 rows and 5 rows fit on the screen at a time. The
if(row == null)
part executes for the first 5 rows only. When you scroll down to the 6th row, it uses the View of the 1st row. This is why you are supposed to set the content of your View after the if-else clause. So now, when the 6th row comes into the screen, row is notnull
. It holds the View of the 1st row. When it executesholder.name.setText(name1);
, it is replacing the name of the 1st row with the name of the 6th row. Similarly, the 7th row uses the view of the 2nd row and so on.Now, suppose you click on the button in the 1st row. The View for the 1st row changes and
w.b.setImageResource(R.drawable.butgreen);
sets the image resource of the button in the 1st row. When you scroll to the 6th row, it uses the View of the 1st row. But you are setting onlyholder.name
to the name of the 6th row. The image resource of the button in the 1st row is going to remain there, hence causing your problem.Solution: Create another
ArrayList<String>
and store the status of the button in this. So in your case, I'm assuming the buttons are either "red" or "green" in color. You could store either "red" or "green" at each position signifying what color each button is currently (you can initialize this ArrayList to red). Then, when each row loads, you can set the image resource along with the name of that row. Take a look at the code below. I have put comments to the lines I added.