wildebeest wildebeest - 1 month ago 7
Android Question

How to stop ListView recycling?

I'm an Android beginner and I can't figure out why this is happening.

Activity Screenshot:

enter image description here

Everything works fine except when I scroll down (hence why I think it has to do with recycling)... So when I scroll back up and attempt to undo the vote (red arrow) on the first post, it thinks the post is voted down!

Alternatively, it may also think the down-vote ImageButton drawable is empty - see code). If I don't scroll, it works perfectly.

getView code:

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
// inflate the list item
convertView = this.inflater.inflate(R.layout.row_layout, parent, false);
// get views
holder.profilePic = (ImageView) convertView.findViewById(R.id.profilePic);
holder.username = (TextView) convertView.findViewById(R.id.username);
holder.day = (TextView) convertView.findViewById(R.id.day);
holder.rating = (TextView) convertView.findViewById(R.id.rating);
holder.textPost = (TextView) convertView.findViewById(R.id.textPost);
holder.ratingUp = (ImageButton) convertView.findViewById(R.id.ratingUp);
holder.ratingDown = (ImageButton) convertView.findViewById(R.id.ratingDown);
convertView.setTag(holder);

} else {
holder = (ViewHolder) convertView.getTag();
}

final Drawable up_clicked = context.getResources().getDrawable(R.drawable.ic_action_rate_up_clicked);
final Drawable up_unClicked = context.getResources().getDrawable(R.drawable.ic_action_rate_up);
final Drawable down_clicked = context.getResources().getDrawable(R.drawable.ic_action_rate_down_clicked);
final Drawable down_unClicked = context.getResources().getDrawable(R.drawable.ic_action_rate_down);

Post post = cityFeed.get(position);
holder.profilePic.setImageResource(post.getDrawableID());
holder.username.setText(post.getUsername());
holder.day.setText(post.getDay());
holder.rating.setText(post.getRating());
holder.textPost.setText(post.getText());

db = new DatabaseHandler(context);
String userVotesUp = null;
userVotesUp = db.getUserVotes("up");
if (userVotesUp == null) {
userVotesUp = "";
}
String userVotesDown = null;
userVotesDown = db.getUserVotes("down");
if (userVotesDown == null) {
userVotesDown = "";
}
String postID = post.getPostID();
if (userVotesUp.contains(postID)) {
holder.ratingUp.setImageDrawable(up_clicked);
} else if (userVotesDown.contains(postID) && userVotesUp != null) {
holder.ratingDown.setImageDrawable(down_clicked);
} else {
holder.ratingUp.setImageDrawable(up_unClicked);
holder.ratingDown.setImageDrawable(down_unClicked);
}
db.close();

holder.ratingUp.setTag(position);
holder.ratingDown.setTag(position);

holder.ratingUp.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View convertView) {

// get post details using button tag
int pos = (Integer)convertView.getTag();
Post post = cityFeed.get(pos);
String postID = post.getPostID();
String ratingString = post.getRating();
int ratingValue = Integer.parseInt(ratingString);
RelativeLayout parent = (RelativeLayout)convertView.getParent().getParent();
TextView ratingView = (TextView)parent.findViewById(R.id.rating);
ImageButton up = (ImageButton)parent.findViewById(R.id.ratingUp);
ImageButton down = (ImageButton)parent.findViewById(R.id.ratingDown);

// if the post is not voted down...
if (down.getDrawable() == down_unClicked) {
// if the post is not voted up...
if (up.getDrawable() == up_unClicked) {
up.setImageDrawable(up_clicked);
ratingValue = ratingValue + 1;
ratingString = Integer.toString(ratingValue);
ratingView.setText(ratingString);
post.setRating(ratingString);
wsAsync = new WebServiceAsync(context);
wsAsync.execute(voteTag, postID, "up");
// else the post is voted up...
} else {
up.setImageDrawable(up_unClicked);
ratingValue = ratingValue - 1;
ratingString = Integer.toString(ratingValue);
ratingView.setText(ratingString);
post.setRating(ratingString);
wsAsync = new WebServiceAsync(context);
wsAsync.execute(voteTag, postID, "down");
}
// else the post is voted down...
} else {
down.setImageDrawable(down_unClicked);
up.setImageDrawable(up_clicked);
ratingValue = ratingValue + 2;
ratingString = Integer.toString(ratingValue);
ratingView.setText(ratingString);
post.setRating(ratingString);
wsAsync = new WebServiceAsync(context);
wsAsync.execute(voteTag, postID, "upDownAlready");
}
}
});

holder.ratingDown.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View convertView) {

// get post details using button tag
int pos = (Integer)convertView.getTag();
Post post = cityFeed.get(pos);
String postID = post.getPostID();
String ratingString = post.getRating();
int ratingValue = Integer.parseInt(ratingString);
RelativeLayout parent = (RelativeLayout)convertView.getParent().getParent();
TextView ratingView = (TextView)parent.findViewById(R.id.rating);
ImageButton up = (ImageButton)parent.findViewById(R.id.ratingUp);
ImageButton down = (ImageButton)parent.findViewById(R.id.ratingDown);

// if the post is not voted up...
if (up.getDrawable() == up_unClicked) {
// if the post is not voted down...
if (down.getDrawable() == down_unClicked) {
down.setImageDrawable(down_clicked);
ratingValue = ratingValue - 1;
ratingString = Integer.toString(ratingValue);
ratingView.setText(ratingString);
post.setRating(ratingString);
wsAsync = new WebServiceAsync(context);
wsAsync.execute(voteTag, postID, "down");
// else the post is voted down...
} else {
down.setImageDrawable(down_unClicked);
ratingValue = ratingValue + 1;
ratingString = Integer.toString(ratingValue);
ratingView.setText(ratingString);
post.setRating(ratingString);
wsAsync = new WebServiceAsync(context);
wsAsync.execute(voteTag, postID, "up");
}
// else the post is voted up...
} else {
up.setImageDrawable(up_unClicked);
down.setImageDrawable(down_clicked);
ratingValue = ratingValue - 2;
ratingString = Integer.toString(ratingValue);
ratingView.setText(ratingString);
post.setRating(ratingString);
wsAsync = new WebServiceAsync(context);
wsAsync.execute(voteTag, postID, "downUpAlready");
}
}
});
return convertView;
}

Answer

By considering that your onClickListerners(on holder.ratingUp and holder.ratingDown) works fine, i think the problem is in this portion of your code

if (userVotesUp.contains(postID)) {
    holder.ratingUp.setImageDrawable(up_clicked);
} else if (userVotesDown.contains(postID) && userVotesUp != null) {
    holder.ratingDown.setImageDrawable(down_clicked);
} else {
    holder.ratingUp.setImageDrawable(up_unClicked);
    holder.ratingDown.setImageDrawable(down_unClicked);
}

If i am not wrong, you are doing something like that you have 3 conditions, First is

if (userVotesUp.contains(postID)) 

that means rating is upvoted and you are setting up_clicked imageDrawable, but you are not setting down_unClicked image drawable to holder.ratingDown.

second condition applies to this

if (userVotesUp.contains(postID)) 

then you are setting down_clicked drawable to holder.ratingDown, but not setting up_unClicked image drawable to holder.ratingUp.

and third condition is neither upvoted nor downvoted and that seems to be fine. so I think, you have to replace your that portion of code with this

if (userVotesUp.contains(postID)) {
    holder.ratingUp.setImageDrawable(up_clicked);
    holder.ratingDown.setImageDrawable(down_unClicked);
} else if (userVotesDown.contains(postID) && userVotesUp != null) {
    holder.ratingDown.setImageDrawable(down_clicked);
    holder.ratingUp.setImageDrawable(up_unClicked);
} else {
    holder.ratingUp.setImageDrawable(up_unClicked);
    holder.ratingDown.setImageDrawable(down_unClicked);
}

Hope this helps..!!

Comments