Reputation: 477
I have a ListFragment that loads asynchronously multiple images but it behave in a strange way, as you can see here.
Here's the AsyncTask code
public class BitmapWorkerClass extends AsyncTask<Integer, Void, Bitmap>
{
private Context context;
private final WeakReference<ImageView> imageViewWeakReference;
private int data = 0;
public BitmapWorkerClass(ImageView imageView, Context context)
{
this.context = context.getApplicationContext();
imageViewWeakReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return ImageResizer.decodeSampledBitmapFromResource(context.getResources(), data, 100,100);
}
@Override
public void onPostExecute(Bitmap bitmap)
{
if(imageViewWeakReference != null && bitmap != null)
{
final ImageView imageView = imageViewWeakReference.get();
if(imageView != null)
{
imageView.setImageBitmap(bitmap);
}
}
}
}
And I call it from the ListFragment adapter's getView() method
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null)
{
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_fragment_single_recipe_title, null);
holder = new ViewHolder();
holder.image = (ImageView) convertView.findViewById(R.id.recipeTitleImage);
holder.title = (TextView) convertView.findViewById(R.id.recipeTitleText);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
Recipe recipe = getItem(position);
loadBitmap(recipe.getImage(), holder.image);
holder.title.setText(recipe.getTitle());
return convertView;
public void loadBitmap(int resId, ImageView imageView)
{
BitmapWorkerClass task = new BitmapWorkerClass(imageView, getContext());
task.execute(resId);
}
Can you help me to understand what makes the AsyncTask behave as in the video?
Thanks in advance
Upvotes: 1
Views: 346
Reputation: 4738
You are probably adding your data in a loop and calling notifyDataSetChanged() within the loop or you are using ArrayAdapters add() method. You possibly also add your data in a different order than displayed.
Therefore convertView gets recycled. This results in getView() for the same item to be called several times.
This causes multiple instances of BitmapWorkerClass to have a WeakReference to the same instance of ImageView. Each of them finish at some point and call OnPostExecute(), causing the shuffle effect.
Using a WeakReference (not recommended btw) helps with memory leaks, but it doesn't help you with View-recycling. The code below should fix the problem you have, however it might not be the most efficient way.
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null)
{
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_fragment_single_recipe_title, null);
holder = new ViewHolder();
holder.image = (ImageView) convertView.findViewById(R.id.recipeTitleImage);
holder.title = (TextView) convertView.findViewById(R.id.recipeTitleText);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
Recipe recipe = getItem(position);
// cancel the previous asynctask if there was any
if (holder.asynctask != null) {
holder.asynctask.cancel(false);
// you could pass true, but would have to handle the interruption then
}
// remove the previous image, you could set a default or loading image here instead
holder.image.setImageDrawable(null);
holder.asynctask = loadBitmap(recipe.getImage(), holder.image);
holder.title.setText(recipe.getTitle());
return convertView;
}
public BitmapWorkerClass loadBitmap(int resId, ImageView imageView)
{
BitmapWorkerClass task = new BitmapWorkerClass(imageView, getContext());
task.execute(resId);
return task;
}
public static class ViewHolder {
ImageView image;
TextView title;
BitmapWorkerClass asynctask; // save a reference to the asynctask
}
Upvotes: 4