0

I'm loading images in a ListView and am using the convertView to add new items. I am finding that when I scroll down quickly, the images from a few screens up are displayed before the current ones.. in the getView I am instantiating an AsyncTask. How do I fix this? Would making an image cache work, or do I have to find a way to communicate with the AsyncTasks that have started and tell them to stop loading the images that won't be shown?

Here is the relevant code:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.list_item_course, null);
    }
    CourseItem courseItem = courseItemList.get(position);

    // ImageView
    Bitmap bm = courseItem.getPhotoBitmap();

    ImageView iv = (ImageView) convertView.findViewById(R.id.imageView1);       
    iv.setAdjustViewBounds(true);
    iv.setMaxHeight(imageWidth);
    iv.setMaxWidth(imageWidth);     

    if (bm != null) {
        iv.setImageBitmap(bm);
    } else {
        CourseItemAndView container = new CourseItemAndView();
        container.courseItem = courseItem;
        container.view = convertView;

        ImageLoader loader = new ImageLoader();
        loader.execute(container);                  
    }   

...

return convertView;
}

private class CourseItemAndView {
    public CourseItem courseItem;
    public Bitmap bitmap;
    public View view;
}

private class ImageLoader extends AsyncTask<CourseItemAndView, Void, CourseItemAndView> {

    @Override
    protected CourseItemAndView doInBackground(CourseItemAndView... params) {

        CourseItemAndView container = params[0];
        CourseItem ci = container.courseItem;

        try {
            String imageUrl = ci.getSmallIcon();
            InputStream in = (InputStream) new URL(imageUrl).getContent();
            Bitmap bitmap = BitmapFactory.decodeStream(in);
            if (bitmap == null)
                System.out.println("HERE");
            ci.setPhotoBitmap(bitmap);
            in.close();
            container.bitmap = bitmap;
            return container;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(CourseItemAndView result) {
        if (result == null) {
            System.out.println("HERE");
        } else {
            ImageView iv = (ImageView) result.view.findViewById(R.id.imageView1);
            iv.setImageBitmap(result.bitmap);
        }
    }

}
Tom McFarlin
  • 829
  • 1
  • 9
  • 24
  • Not only should they stop when the imageview is out of sight but also they should only load their bitmap in the right imageview. You did not show any code which handles that. Nor did you tell. Please post complete getView code. – greenapps Sep 24 '14 at 16:34
  • If the convertView is not null I reuse that view, so when I scroll say 2 screens, two different bitmaps are being loaded to the same convertView's ImageView child. There is no more useful code in the getView other than "return convertView"... I'll try and post other relevant code. – Tom McFarlin Sep 24 '14 at 17:27

1 Answers1

0

In order that the async task knows the item position for the listview so it can act accordingly when it wants to put the bitmap in you add a position and listview parameter to the asynctask. And tag the imageview with the asynctask instance.

  ImageLoader loader = new ImageLoader(listView, position);
  container.view.setTag(loader);
  loader.execute(container);     

Then in onPostExecute the bitmap does not have to be placed when position is outside the visible items of the listview.

  if( position < listView.getFirstVisiblePosition() 
     || position > listView.getLastVisiblePosition()
     )
      return;

Then check if the task belonged to the image view with:

if ( imageView.getTag() != this )
     return;

.... place bitmap ..
greenapps
  • 11,154
  • 2
  • 16
  • 19