Im using RecyclerView
to display a list containing an imageView
. To make the UI more fluently, I load 58dp thumbnails saved on sd card into these i
Due to view reuse you'll fetch views with content already on them, this was a problem on ListViews
too if you were using the ViewHolder
pattern, which you should.
There are two solutions here, the good practice and the bad hack:
In the good practice you set your ImageView
to display nothing at the
beginning of bindViewHolder(VH holder, int position)
using
setDrawable(null)
or similar.
In the bad hack you wouldn't recycle/reuse views, not enforcing the ViewHolder
pattern, and you'd inflate it every time, but that's only allowed in ListView
and other old components.
What MLProgrammer says is quite correct.
The solution, fortunately, it's easy: stop the recycling
holder.setIsRecyclable(false);
This choice has its consequences, since doing what I suggest above inhibits one of RecyclerView main purposes: recycling.
I would add to the good practice:
In the good practice you set your ImageView to display nothing at the beginning of bindViewHolder(VH holder, int position) using setDrawable(null) or similar.
not to display nothing, but to display a loader image so to give the user a feedback that theres some processing going on in that view and in a little while it will see the results. Seeing just a blank view is not good practice, you need to give feedback to the user.
you must cancel old request in "onBindViewHolder" method:
try{
((SpecialOfferViewHolder)viewHolder).imageContainer.cancelRequest();
}catch(Exception e) {
}
remember to save image container in the viewHolder:
public void onResponse(ImageContainer response, boolean arg1) {
((SpecialOfferViewHolder)viewHolder).imageContainer=response;
}
You should cancel the old request before starting a new one, but regardless of cancelling you can still show the wrong image if both images loaded more or less at the same time on the same container/view holder that has been recycled (happens easily with fast scroll and small images).
The solution is to:
Example loading icons from apps in background with RxJava:
public void loadIcon(final ImageView appIconView, final ApplicationInfo appInfo, final String uniqueAppID) {
Single.fromCallable(() -> {
return appIconView.getContext().getPackageManager().getApplicationIcon(appInfo);
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( drawable -> {
if (uniqueAppID.equals(mUniqueAppID)) { // Show always the correct app icon
appIconView.setImageDrawable(drawable);
}
}
);
}
Here mUniqueAppID is a field of the view holder changed by onBindViewHolder
You should use Picasso. A powerful image downloading and caching library for Android. Easy to use and a powerful library. Using this library you can fetch images asynchronously or synchronously from Resources, assets, files, content providers.