StackOverflowed
StackOverflowed

Reputation: 5975

Android Volley - Cancel NetworkImageView request in Base Adapter

As the title says, I'm using a BaseAdapter to display items in a ListView. Obviously a ListView will reuse views, including TextViews and NetworkImageViews.

Assuming 3 items can be displayed at once, the NetworkImageView will be reused for items at index: 1, 4, 7, ....

Depending on what's being displayed, the NetworkImageView will either:

  1. request the image from the Network and display it,
  2. display a cached Bitmap,
  3. or display a local drawable resource.

Items 2 and 3 work fine, however in Scenario 1, let's say we're displaying item at index 4 from the network, and the user scrolls to item 7 before 4 is loaded and it's a local resource, we display the local resource. However our network image request may just be finishing now, so we end up displaying an incorrect image.

How can I enforce the proper (expected)behavior?

Upvotes: 1

Views: 1625

Answers (3)

Pork 'n' Bunny
Pork 'n' Bunny

Reputation: 6731

You don't need to enforce anything if you use the provided NetworkImageView.

NetworkImageView detects when it has been recycled and cancels the request automatically.

Upvotes: 3

MH.
MH.

Reputation: 45493

The answer from @Snicolas is spot on, but lacks some pointers on how to actually accomplish that. So here goes.

The general idea is to keep track of the ongoing image requests for every row. That way, when you encounter a recycled row, you can cancel the pending request and kick off a new one for the new data relevant to that row.

One straightforward way to accomplish that is to make the ImageContainer that you can get back when requesting an image load, part of the adapter's ViewHolder/RowWrapper. If you're not using this pattern yet, you should. Plenty of examples out there, including a good I/O talk.

Once you've added the ImageContainer to your holder, make an image request and store the container that you get back. Somewhat like this:

ImageListener listener = ImageLoader.getImageListener(holder.imageview, defaultImageResId, errorImageResId);
holder.mImageContainer = ImageLoader.get(url, listener);

The next time a recycled row comes in the adapter's getView() method, you can get your holder back from it and check wether it has a ImageContainer set. One of the following 3 scenarios may apply:

  • There is no ImageContainer, which means you're good to go to make a new image request.
  • There is an ImageContainer and the url that it is loading is the same as for the new row data. In this case you don't have to do anything, since it's already loading the image you're after.
  • There is an ImageContainer but the url that it is loading is different from the new row data. In this case, cancel the request and make a new one for the current row data.

If you like, you can move some of this logic by having your BaseAdapter extension implement AbsListView.RecyclerListener (and set the adapter as recycler listener for the ListView or GridView). The onMovedToScrapHeap(View view) method gets passed in the view that has just been recycled, which means you can cancel any pending image requests in there.

Upvotes: 5

Snicolas
Snicolas

Reputation: 38168

I don't know this API but my guess is that you should cancel any pending request before recycling such a view. How you can do that I can't say.

Did you here of alternatives like :

Upvotes: -1

Related Questions