I have a listview which loads images in every cell in async. When I try to scroll down slowly(after all the images in the current view are loaded), it works flawlessly. But when I try to scroll down before they are even loaded and scroll up, I face this issue. The cells start to show up images which don't correspond to them.
My getView method looks like this:
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View rowView = null;
if(convertView == null) {
rowView = inflater.inflate(R.layout.list_posts_item, null);
final Holder holder=new Holder();
holder.tvTitle=(TextView) rowView.findViewById(R.id.tvTitleNamePost);
holder.ivPrimaryImage=(ImageView) rowView.findViewById(R.id.ivPrimaryImage);
holder.tvLocality=(TextView) rowView.findViewById(R.id.tvLocalityPosts);
holder.tvDateCreated=(TextView) rowView.findViewById(R.id.tvDateCreated);
rowView.setTag(holder);
}else {
rowView=convertView;
}
Holder holder = (Holder)rowView.getTag();
holder.ivPrimaryImage.setId(position);
holder.ivPrimaryImage.setTag(listOfPosts.get(position).getPostId());
holder.ivPrimaryImage.setImageBitmap(null); // Added for flickering issue
holder.tvTitle.setText(listOfPosts.get(position).getTitle());
holder.tvLocality.setText(listOfPosts.get(position).getLocality());
holder.tvDateCreated.setText(listOfPosts.get(position).getCreatedDate());
postId = listOfPosts.get(position).getPostId();
Image image = new Image();
image.setImg(holder.ivPrimaryImage);
if (!"N".equalsIgnoreCase(listOfPosts.get(position).getHasImage()) ) {
if(!tagsCaching.containsKey(postId))
new GetPrimaryImages().execute(image);
else
holder.ivPrimaryImage.setImageBitmap(tagsCaching.get(postId));
}
return rowView;
}
And my Async call class looks like this:
public class GetPrimaryImages extends AsyncTask<Image, Void, Bitmap> {
ImageView imageView = null;
protected Bitmap doInBackground(Image... images) {
this.imageView=images[0].getImg();
// Building Parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("postid",(String)(this.imageView.getTag()) ));
json = jsonParser.makeHttpRequest(CommonResources.getURL("get_primary_image"),
"POST", params);
if(json == null){
return null;
}
Log.d("Fetching Image",imageView.getTag()+ json.toString());
tagsDownloaded.add((String)imageView.getTag());
// check for success tag
String TAG_SUCCESS = "success";
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 0) {
image = json.getString("primaryimage");
}
} catch (JSONException e) {
e.printStackTrace();
}
return getImage(image);
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(Bitmap result) {
tagsCaching.put((String)imageView.getTag(), result);
imageView.setImageBitmap(result);
}
public Bitmap getImage(String imageString) {
if("null".equalsIgnoreCase(imageString)){
return null;
}else{
byte[] decodedString = Base64.decode(imageString, Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
//image.setImageBitmap(decodedByte);
return decodedByte;
}
}
}
Edit:
I added a new instance variable to Holder:
public class Holder
{
TextView tvTitle;
ImageView ivPrimaryImage;
TextView tvLocality;
TextView tvDateCreated;
int position;
}
Set the same in the getView: holder.position = position; And passed the holder object to the Async task: new GetPrimaryImages(position, holder).execute(image);
And modified the Async call class as follows: 1. Added cancel to the http call 2. Changed the onPostExecute method
public class GetPrimaryImages extends AsyncTask<Image, Void, Bitmap> {
int mPosition;
Holder mHolder;
public GetPrimaryImages(int position, Holder holder){
mPosition = position;
mHolder = holder;
}
ImageView imageView = null;
protected Bitmap doInBackground(Image... images) {
this.imageView=images[0].getImg();
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("postid",(String)(this.imageView.getTag()) ));
JSONObject json;
if(mHolder.position == mPosition)
json = jsonParser.makeHttpRequest(CommonResources.getURL("get_primary_image"),
"POST", params);
else {
json = null;
cancel(true);
}
// check log cat fro response
if(json == null){
return null;
}
Log.d("Fetching Image",imageView.getTag()+ json.toString());
tagsDownloaded.add((String)imageView.getTag());
// check for success tag
String TAG_SUCCESS = "success";
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 0) {
image = json.getString("primaryimage");
}
} catch (JSONException e) {
e.printStackTrace();
}
return getImage(image);
}
protected void onPostExecute(Bitmap result) {
if (mHolder.position == mPosition) {
tagsCaching.put((String) imageView.getTag(), result);
imageView.setImageBitmap(result);
}
}
public Bitmap getImage(String imageString) {
//needs to wait
if("null".equalsIgnoreCase(imageString)){
return null;
}else{
byte[] decodedString = Base64.decode(imageString, Base64.DEFAULT);
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
//image.setImageBitmap(decodedByte);
return decodedByte;
}
}
}
It seems to be working. :)
Now my doubt is what would be the best way to cache the images? Should be writing it to a file? and reading it from it every time I scroll up?