That behavior appears because the ListView
recycles the row views as you scroll the list up and down, and because of this you get rows that were acted on by the user(the image was changed) in position were the image should be unmodified. To avoid this you'll have to somehow hold the status of the ImageView
for every row in the list and use this status to set up the correct image in the getView()
method. Because you didn't say how exactly did you implement your adapter I will show you a simple example.
First of all you should store your the statuses of the ImageView
. I used an ArrayList<Boolean>
as a member of the custom adapter, if the position(corresponding to the row's position in the list) in this list is false
then the image is the default one, otherwise if it is true
then the user clicked it and we should put the new image:
private ArrayList<Boolean> imageStatus = new ArrayList<Boolean>();
In your custom adapter constructor initialize this list. For example if you put in your adapter a list of something then you should make your imageStatus
as big as that list and filled with false
(the default/start status):
//... initialize the imageStatus, objects is the list on which your adapter is based
for (int i = 0; i < objects.size(); i++) {
imageStatus.add(false);
}
Then in your getView()
method:
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.adapters_adapter_with_images, null);
}
// find the image
ImageView favImage = (ImageView) v
.findViewById(R.id.toggle_favorite);
// Set the image bitmap. If the imageStatus flag for this position is TRUE then we
// show the new image because it was previously clicked by the user
if (imageStatus.get(position)) {
int newImage = R.drawable.ic_star_yellow_embossed;
favImage.setImageBitmap(BitmapFactory.decodeResource(
getContext().getResources(), newImage));
} else {
// If the imageStatus is FALSE then we explicitly set the image
// back to the default because we could be dealing with a
// recycled ImageView that has the new image set(there is no need to set a default drawable in the xml layout)
int newImage = R.drawable.basket_empty; //the default image
favImage.setImageBitmap(BitmapFactory.decodeResource(
getContext().getResources(), newImage));
}
// when clicked... we get the real position of the row and set to TRUE
// that position in the imageStatus flags list. We also call notifyDataSetChanged
//on the adapter object to let it know that something has changed and to update!
favImage.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Integer realPosition = (Integer) v.getTag(); //get the position from the view's tag
imageStatus.set(realPosition, true); //this position has been clicked be the user
adapter.notifyDataSetChanged(); //notify the adapter
}
});
// set the position to the favImage as a tag, we later retrieve it
// in the onClick method
favImage.setTag(new Integer(position));
return v;
}
This should work well if you don't plan to dynamically modify the list(remove/add rows), otherwise you'll have to take care of also modifying that list of imageStatus
to reflect the changes. You didn't say what was your row data, another approach(and the right one if you plan to do something if the user clicks that image(besides changing it)) is to incorporate the status of the image in the row's data model. Regarding this here are some tutorials:
Android ListView Advanced Interactive
or Commonsware-Android Excerpt (Interactive rows)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…