通用图像加载器:错误的位图被附加到ImageView上。

11

我一直在评估 NOSTRAUniversal-Image-Loader 库,用于异步下载图片并在 ListView 中显示。到目前为止,它的功能都很好,除了一个问题。

当列表在滚动时,有时来自内存缓存的 Bitmaps 会被附加到错误的 ImageViews 上。当滚动停止后,正确的图像将被附加。这种情况相当罕见,我找不到一种 100% 的方法来重现它。上次它发生时,我拍摄了一个 视频

这里是 ArticleAdapter 代码,其中包含 UIL 配置和 bindView() 方法。

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;
    }
}

我非常希望能得到关于这件事的任何帮助。


@samintechvalens 请不要加粗关键词,如果您想突出代码相关的关键词,请使用反引号进行内联。 - Rudi Visser
你使用过 PauseOnScrollListener 吗? - nostra13
@NOSTRA 是的,我使用它。 - Timur D.
5个回答

20
对于使用视图重用的 ListView、GridView 和其他列表,其适配器中应该在 DisplayImageOptions 中调用 .resetViewBeforeLoading() 以防止这种情况发生。 此外,文档中还指出:

只需使用配置初始化 ImageLoader 一次即可

你只做了一次吗?适配器的构造函数不是很好的位置。 更新:抱歉,我的回答没有用。 .resetViewBeforeLoading() 没有帮助,因为您使用了 .showStubImage(...)。所以您应该拥有正确的 UIL 工作,但实际上却没有。这很奇怪。

是的,我刚刚完成了。我会测试这个应用程序几天,看看是否正常工作。 - Timur D.
我猜我找到了问题所在。我最终在应用程序中两次初始化了ImageLoader。我这样做的原因是我需要两个不同的配置:一个在ListActivity中,另一个在显示文章的Activity中。我会重构它并稍后再回复你。 - Timur D.
这个问题的正确答案是:不要多次初始化UIL。感谢您的帮助。 - Timur D.
实际上,ImageLoader 的第二次及其后的初始化都没有任何作用。在您的应用程序中,只有第一个配置起作用,第二个配置不被考虑。如果您想要具有两个或更多不同配置的 ImageLoader,则应扩展 ImageLoader 类。 - nostra13
不,它不会覆盖。 - nostra13
显示剩余7条评论

3
我经常遇到这个问题,即使我只在初始化ImageLoader一次,但我没有仅在需要它时(在适配器中)使用它,更改Application类中的init()部分后,它表现出色。我甚至没有使用restartViewOnLoading()或setStubImage()。如果需要,这是代码。
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;
    }
}

编辑:您可以在此处查看有关该问题的更深入理解的对话。

基本上有三种解决方案:

1)以dips为单位设置ImageView的android:layout_width和android:layout_height参数('wrap_content'和'match_parent'不可接受)

2)在ImageView绘制后调用ImageLoader(在imageView.post(...)中):

imageView.post(new Runnable() {
        @Override
        public void run() {
            imageLoader.displayImage(imageUri, imageView);
        }
     });

3)传递ImageViewAware(而不是ImageView),它不考虑实际视图大小:

而不是:

imageLoader.displayImage(imageUri, imageView);

做以下操作:
ImageAware imageAware = new ImageViewAware(imageView, false)
imageLoader.displayImage(imageUri, imageAware);

0

恐怕不是这样。只有图片存在问题,物品上的其他所有内容(标题和日期)都正确显示。 - Timur D.
嗯..你尝试过在imageLoader.displayImage中使用SimpleImageLoadingListener吗?因为它会给你一些通知,比如onLoadingCancelledonLoadingCompleteonLoadingStartedonLoadingFailed。你可以在这些监听器中执行其他更改。 - Chintan Rathod

-1

我曾经遇到过同样的问题并解决了它。这不是因为Universal-Image-Loader库,而是因为您在加载图像时使用了错误的逻辑来使用holder。

尝试替换

    @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;
    }

@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;
    }

记得在bindView函数中移除imageloader


如果为每个列表项调用newView(),则holder的概念就会被抛弃!因此,这不是正确的方向。 - Sarang

-1
在你的代码中添加这行 ::
holder.thumbView.setTag(Utils.makeImageUrl(url, Utils.THUMBNAIL).get(position));
imageLoader.displayImage(Utils.makeImageUrl(url, Utils.THUMBNAIL), view_holder.image);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接