问题
I've been evaluating NOSTRA's Universal-Image-Loader library to asynchronously download images and show them in ListView. So far it works fine except for one problem.
Sometimes Bitmaps from memory cache get attached to wrong ImageViews
when the list is being scrolled. After scrolling is stopped, correct images are attached. This situation is quite rare and I couldn't find a 100% way to reproduce it. I shot a video last time it happened.
Here is the ArticleAdapter
code, both the UIL config and the bindView()
method can be found there.
public class ArticleAdapter extends CursorAdapter {
private LayoutInflater inflater;
private ViewHolder holder;
public ArticleAdapter(Context context, Cursor cursor, boolean autoRequery) {
super(context, cursor, autoRequery);
imageLoader = ImageLoader.getInstance();
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.download_progress_thumb)
.cacheInMemory()
.cacheOnDisc()
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
.build();
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(context)
.threadPriority(Thread.NORM_PRIORITY - 2)
.threadPoolSize(4)
.discCache(new UnlimitedDiscCache(Utils.getCacheDirectory(context)))
.defaultDisplayImageOptions(options)
.build();
imageLoader.init(configuration);
titleIndex = cursor.getColumnIndex(Articles.TITLE);
descriptionIndex = cursor.getColumnIndex(Articles.DESCRIPTION);
isUnreadIndex = cursor.getColumnIndex(Articles.IS_UNREAD);
isNewIndex = cursor.getColumnIndex(Articles.IS_NEW);
urlIndex = cursor.getColumnIndex(Articles.URL);
hostIndex = cursor.getColumnIndex(Articles.HOST);
timeIndex = cursor.getColumnIndex(Articles.PUBLISH_TIME);
bkgUnreadArticle = context.getResources().getColor(R.color.list_bkg_unread_article);
bkgReadArticle = context.getResources().getColor(R.color.list_bkg_read_article);
textUnreadTitle = context.getResources().getColor(R.color.list_text_unread_title);
textReadTitle = context.getResources().getColor(R.color.list_text_read_title);
inflater = LayoutInflater.from(context);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
String date = Utils.format(cursor.getLong(timeIndex), Utils.DATE);
holder = (ViewHolder) view.getTag();
holder.titleView.setText(cursor.getString(titleIndex));
holder.descriptionView.setText(date);
int isNew = cursor.getInt(isNewIndex);
if (isNew == 1)
holder.isNewView.setVisibility(View.VISIBLE);
else
holder.isNewView.setVisibility(View.INVISIBLE);
int isUnread = cursor.getInt(isUnreadIndex);
if (isUnread == 1){
holder.titleView.setTextColor(textUnreadTitle);
holder.rowLayout.setBackgroundColor(bkgUnreadArticle);
} else {
holder.titleView.setTextColor(textReadTitle);
holder.rowLayout.setBackgroundColor(bkgReadArticle);
}
String url = cursor.getString(urlIndex);
String host = cursor.getString(hostIndex);
if (host.equalsIgnoreCase(Consts.HOST_LENTA) || host.equalsIgnoreCase(Consts.HOST_REALTY)) {
holder.thumbView.setVisibility(View.VISIBLE);
imageLoader.displayImage(Utils.makeImageUrl(url, Utils.THUMBNAIL), holder.thumbView);
} else
holder.thumbView.setVisibility(View.GONE);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.articlelist_item, null);
ViewHolder holder = new ViewHolder();
holder.titleView = (TextView) v.findViewById(R.id.list_title);
holder.descriptionView = (TextView) v.findViewById(R.id.list_description);
holder.thumbView = (ImageView) v.findViewById(R.id.list_thumb);
holder.isNewView = (TextView) v.findViewById(R.id.list_read_unread);
holder.rowLayout = (LinearLayout) v.findViewById(R.id.list_row);
v.setTag(holder);
return v;
}
}
I would really appreciate any help on this matter.
回答1:
For ListViews, GridViews and other lists which are used view re-using in its adapters you should call .resetViewBeforeLoading()
in DisplayImageOptions to prevent this effect.
Also documentation says:
Init ImageLoader with configuration only once
Do you do it only once? Adapter's constructor isn't good place for it.
UPD: Sorry, my answer isn't useful. .resetViewBeforeLoading()
doesn't help because you use .showStubImage(...)
. So you should have correct UIL work but you don't. And it's very strange.
回答2:
I had this problem on a regular basis, even though I was only initiating the ImageLoader once, I wasn't doing it only when I needed it (in the adaptor), after I changed the init() part in Application class it worked brilliantly. I haven't even had to use restartViewOnLoading() or setStubImage(). Here's the code if necessary.
import android.content.Context;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
public class Application extends android.app.Application {
private static Context mContext;
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
DisplayImageOptions imgOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.showImageOnLoading(R.drawable.default_picture)
.build();
ImageLoaderConfiguration imgConfig = new ImageLoaderConfiguration.Builder(mContext)
.defaultDisplayImageOptions(imgOptions)
.build();
ImageLoader.getInstance().init(imgConfig);
}
public static Context getAppContext(){
return mContext;
}
}
EDIT: You can check this conversation here for a deeper understanding of the issue. Basically there are 3 solutions
1) Set android:layout_width and android:layout_height parameters for ImageViews in dips ('wrap_content' and 'match_parent' are not acceptable)
2) Call ImageLoader after ImageView was drawn (in imageView.post(...):
imageView.post(new Runnable() {
@Override
public void run() {
imageLoader.displayImage(imageUri, imageView);
}
});
3) Pass ImageViewAware (instead of ImageView) which doesn't consider actual view size:
Intead of:
imageLoader.displayImage(imageUri, imageView);
do following:
ImageAware imageAware = new ImageViewAware(imageView, false)
imageLoader.displayImage(imageUri, imageAware);
回答3:
Just see how to set Holders because I think you have written faulty logic inside your Adapter
thats why it is repeating views.
There is also Custom Cursor Adapter with Holder and Get View & BindView discussion.
回答4:
Add this line in your code ::
holder.thumbView.setTag(Utils.makeImageUrl(url, Utils.THUMBNAIL).get(position));
imageLoader.displayImage(Utils.makeImageUrl(url, Utils.THUMBNAIL), view_holder.image);
回答5:
I have same problem and fixed it. It is not because Universal-Image-Loader library. It is because you use holder in wrong logic to load image.
Try to replace
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.articlelist_item, null);
ViewHolder holder = new ViewHolder();
holder.titleView = (TextView) v.findViewById(R.id.list_title);
holder.descriptionView = (TextView) v.findViewById(R.id.list_description);
holder.thumbView = (ImageView) v.findViewById(R.id.list_thumb);
holder.isNewView = (TextView) v.findViewById(R.id.list_read_unread);
holder.rowLayout = (LinearLayout) v.findViewById(R.id.list_row);
v.setTag(holder);
return v;
}
With
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(R.layout.articlelist_item, null);
ViewHolder holder = new ViewHolder();
holder.titleView = (TextView) v.findViewById(R.id.list_title);
holder.descriptionView = (TextView) v.findViewById(R.id.list_description);
ImageView thumbView = (ImageView) v.findViewById(R.id.list_thumb);
imageLoader.displayImage("Your image URL", thumbView);
holder.isNewView = (TextView) v.findViewById(R.id.list_read_unread);
holder.rowLayout = (LinearLayout) v.findViewById(R.id.list_row);
v.setTag(holder);
return v;
}
And remember to remove imageloader in your bindView function
来源:https://stackoverflow.com/questions/14080117/universal-image-loader-wrong-bitmaps-are-attached-to-imageview