Search code examples
javaandroiduniversal-image-loader

Universal-Image-Loader: wrong Bitmaps are attached to ImageView


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.


Solution

  • 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.