ListView回收图像

时间:2014-01-17 15:17:59

标签: android lazy-loading android-imageview

我有一个自定义ArrayAdapter,可以从网络上获取图像。我知道这些观点得到了回收。我的代码似乎有效,但是从网络加载的图像存在问题。有时,错误的图像可能会显示另一行。例如,米老鼠可能是第0行上的图像,当我向下滚动时,在转换为唐老鸭之前,第9行(示例)可能会短暂出现米老鼠。当我向后滚动到顶部时,唐老鸭可能会出现在第0行,然后再换回米老鼠。

这是我的代码:

class OffersCustomAdapter extends ArrayAdapter<Merchant>{

           Context context;
           ArrayList<User> userName;
           private LayoutInflater inflater;
           private ImageLoadingListener animateFirstDisplayListener;
           private ImageLoader imageLoader;

public OffersCustomAdapter(Context c, ArrayList<User> users) {

    super(c, R.layout.single_row, users);
    this.context=c;
    this.userName=users;
    imageLoader = ImageLoader.getInstance();
    imageLoader.init(ImageLoaderConfiguration.createDefault(context));

}
static class ViewHolder{
        TextView title;
        TextView cat;
        TextView type;
        TextView desc;
        ImageView pic;
}

@Override
public int getViewTypeCount() {                 


    return 1;
}

@Override
public int getItemViewType(int position) {

    return position;
}


@Override                                                   //parent is listview
public View getView(int position, View convertView, ViewGroup parent) {
    View row=convertView;
    ViewHolder viewHolder;
    if(convertView == null){
    inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    //row contains our relative layout
    row =inflater.inflate(R.layout.single_row, parent, false);
    viewHolder = new ViewHolder();
    viewHolder.title =
            (TextView) row.findViewById(R.id.textView1);

    viewHolder.pic = (ImageView) row.findViewById(R.id.imageView1);
    row.setTag(viewHolder);
    }else{
        viewHolder = (ViewHolder) convertView.getTag();
    }

    viewHolder = (ViewHolder) row.getTag();
    User u = userName.get(position);
    String titleSt = userName.get(position).getName();
    viewHolder.title.setText(titleSt);
    imageLoader.displayImage(imageUrl+userName.get(position).getImg(), viewHolder.pic, animateFirstDisplayListener);


    return row;
}

我看过SO的其他例子,但没有运气。

4 个答案:

答案 0 :(得分:3)

这是因为视图正在被回收。向下滚动时,出现的下一个视图将使用刚刚滚出视图的相同视图(即米老鼠)。您可以在imageLoader获取新图片时显示加载图片来解决此问题。

如果您没有加载图片,可以在getView(...)方法的开头执行以下操作:

viewHolder.pic.setImageDrawable(null);

编辑:根据评论修复。

答案 1 :(得分:1)

这可能是因为如果在视图被回收时仍在进行中,之前的图像加载操作不会被取消,因此这会在设置视图的位图时引入竞争条件。本文将简要讨论此问题,可能值得一读:Multithreading for Performance

作者更详细地解释了它:

  

但是,特定于ListView的行为会显示我们的问题   目前的实施。的确,出于内存效率的原因,   ListView回收用户滚动时显示的视图。   如果一个人翻转列表,则会使用很多给定的ImageView对象   倍。每次显示时,ImageView都会正确触发   图像下载任务,最终将改变其图像。那么在哪里   是问题吗?与大多数并行应用程序一样,关键问题是   在订购中。在我们的例子中,不保证下载   任务将按照启动顺序完成。结果   是最终显示在列表中的图像可能来自a   上一个项目,它恰好花了更长的时间下载。   如果您下载的图像被绑定一次,那么这不是问题   所有给予ImageViews,但让我们解决它的常见情况   它们用在列表中。

并提供可能有帮助的解决方法示例。

答案 2 :(得分:0)

尝试picasso

在工作区中安装jar后,您只需要一行代码。

替换imageLoader.displayImage(imageUrl+userName.get(position).getImg(), viewHolder.pic, animateFirstDisplayListener);

Picasso.with(context).load(your_image_Url).into(viewholder.pic);

我认为your_image_url <{1}}就是imageUrl+userName.get(position).getImg();

答案 3 :(得分:0)

有一篇为期5年的Android博文,介绍了如何手动解决这个问题,Multithreading for Performance

如今,您应该使用Picasso或Volley进行图像加载。这些网络API可以轻松解决您的问题,并为您提供额外的好处,例如缓存。 Volley是Google在自己的应用程序中使用的API。我在我的应用程序中使用它并且是粉丝。

查看thorough introduction to both frameworks