在列表视图中加载图像时加载错误的图像

Posted

技术标签:

【中文标题】在列表视图中加载图像时加载错误的图像【英文标题】:Wrong Image Loads when loading images in listview 【发布时间】:2012-07-25 15:20:54 【问题描述】:

我见过几个这样的问题,但我似乎遇到了一个稍微不同的问题。所以我想我会在这里问一个人。

我有一个带有 textview 和 imageview 的自定义列表视图。我正在使用异步任务加载图像,然后在我的 post execute 方法中将图像设置为 imageview。我的图像加载正常,但某些图像被其他图像替换。但是在所有图像加载完成后,每个图像视图都有正确的图像。我似乎无法弄清楚为什么我有这个问题。

这是我的适配器和异步任务类。任何和所有的帮助将不胜感激。

EDIT 我现在添加一个标签(标签是进入图像视图各自文本视图的文本),在我的帖子执行中,我正在检查标签是否等于文本视图中的文本.我仍然遇到和以前一样的问题。

这是我的适配器类:

public class CustomListAdapter extends ArrayAdapter<CustomList> 

    Context context;
    int layoutResourceId;
    LinkedList<CustomList> data = null;
    LinkedList<String> title_list = new LinkedList();

    LoadImage l;
    CustomList cl;
    ProgressBar pb;
    HashMap <String, Bitmap> bitmap = new HashMap<String, Bitmap>();







    public CustomListAdapter(Context context, int layoutResourceId, LinkedList<CustomList> data, LinkedList<Bitmap> bitmap_list) 
        super(context, layoutResourceId, data);
        // TODO Auto-generated constructor stub

        this.context = context;
        this.layoutResourceId = layoutResourceId;
        this.data = data;

    

    @Override
    public View getView(int position, View convertView, ViewGroup parent) 
        // TODO Auto-generated method stub

        View row = convertView;
        CustomListHolder holder = null;




        if(row == null)
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, parent, false);
            holder = new CustomListHolder();

            holder.text = (TextView)row.findViewById(R.id.txtTitle);
            holder.thumbnail = (ImageView)row.findViewById(R.id.imgIcon);
            holder.pb = (ProgressBar)row.findViewById(R.id.progressBar1);

            row.setTag(R.id.id0, holder);


            cl = data.get(position);
            holder.text.setText(cl.title);
            holder.thumbnail.setImageResource(R.drawable.icon);
            holder.pb.setVisibility(View.VISIBLE);


            row.setTag(R.id.id1,new String(cl.title));
            LoadImage li = new LoadImage(context, holder.thumbnail, cl.icon,cl.title, bitmap,holder.pb,row);
            li.execute(cl.icon);        

        

        else
            Log.e("Row not null","Inside");
            holder = (CustomListHolder)row.getTag(R.id.id0);
            //row.getK
            cl = data.get(position);        
            holder.text.setText(cl.title);
            holder.thumbnail.setImageResource(R.drawable.icon);
            holder.pb.setVisibility(View.VISIBLE);
            if((Bitmap) bitmap.get(cl.title) == null)
                row.setTag(R.id.id1,new String(cl.title));
              LoadImage li = new LoadImage(context, holder.thumbnail, cl.icon,cl.title, bitmap,holder.pb,row);
                li.execute(cl.icon); 


            
            else 
                holder.thumbnail.setImageBitmap((Bitmap) bitmap.get(cl.title));
                holder.pb.setVisibility(View.GONE);
            



           



        return row;


    

    static class CustomListHolder
    
        ImageView thumbnail;
        TextView text;
        ProgressBar pb;
    



这是我的异步任务类:

公共类 LoadImage 扩展 AsyncTask

Context callingContext = null;
ImageView view;
String bits;
public  ProgressBar pb;
HashMap<String, Bitmap> bitmap; 
String url;
String text;
View row;



public LoadImage(Context c, ImageView view, String bits, String text, HashMap<String, Bitmap> bitmap, ProgressBar pb, View row)

    this.view = view;
    this.bits = bits; // url for image
    this.callingContext = c;
    this.bitmap = bitmap; //hashmap
    this.text = text;// title text
    this.pb = pb;
    this.row = row;





public Bitmap getBitmap(String data)

    Bitmap bitmap;
    BitmapFactory.Options bmOptions;
    bmOptions = new BitmapFactory.Options();
    bmOptions.inJustDecodeBounds = true;
    Log.e("getBitmap",text);

    try 
        bitmap=null;
        InputStream is=new URL(data).openStream();
        BitmapFactory.decodeStream(is, null, bmOptions);
        is.close();
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = 10;
        is = new URL(data).openStream();
        bitmap = BitmapFactory.decodeStream(is, null, o2);

        bitmap = Bitmap.createScaledBitmap(bitmap, 60, 60, true);
        is.close();
        this.bitmap.put(text,bitmap);
        return bitmap;
     catch (Exception ex)
        Log.e("Debug", ex.getMessage());
       return null;
    




@Override
protected Bitmap doInBackground(String... arg0) 
    // TODO Auto-generated method stub
    Log.e("do In Bg",text);
    Bitmap b = null;    

    if((Bitmap)bitmap.get(text) == null)
      b = getBitmap(bits);
    else 
      b =(Bitmap)bitmap.get(text);

        return b;  




@Override
protected void onPostExecute(Bitmap result) 
    // TODO Auto-generated method stub
    super.onPostExecute(result);
        if(row.getTag(R.id.id1).equals(text))
            view.setImageBitmap((Bitmap)bitmap.get(text));
            pb.setVisibility(View.GONE);
        




   

CustomList 是一个具有 2 个字符串图标和标题的类。 icon 是图像的 url,title 是 textview 的文本。

【问题讨论】:

你的 xml 是什么样的?图像视图中有临时图像吗?听起来像你这样做,当asynctask 完成加载时,你会得到正确的图像。 只有滚动时才会出现吗? @BlaineOmega - 我在加载图像时将图像设置为等于 android 启动器。加载完成后,启动器将被图像替换。我正在加载 2 个不同的图像,可以说 a 和 b 大约 10 次。在我的列表视图中,我有交替的图像。即使在图像加载后,有时 b 也会占据 a 的位置,反之亦然。但是,在所有图像完全加载后,图像仍处于正确位置。 @Krylez - 这发生在滚动和静止位置时。 好的,还有一个问题;如果您根本不滚动,它仍然会发生吗? 【参考方案1】:

我确定@Krylez 会告诉你同样的事情:在 ListView 中,你的行会被回收。这是covertView 给你的,你做一个row==null 检查。如果您的屏幕一次显示 10 行,那么它的内存中可能大约有 11 到 12 行。当您滚动并且一行消失时,它会保存在内存中,并与旧内容一起返回给您,供您重新填充。

假设您有单元格 1...您要求位图 1。然后用户将其滚动出视图并将其作为位图 12 回收。现在位图 1 任务完成并在其上绘制,因为对象引用仍然完好无损(从未被摧毁)。因此,您会得到显示错误图像的这种效果。

我在一个 ios 项目上使用了一个简单的解决方法:使用对象标签。让我解释一下:

当您启动 LoadImage 任务时,使用唯一 ID 标记您的行,您可以识别位图及其与...的位置关系...

row.setTag(new Integer(ID_THAT_IDENTIFIES_ROW_AND_BITMAP_RELATIONSHIP))

在异步任务中执行 onPostExecute() 时,请检查此标识符是否已更改。如果您当前的位图不再匹配,请不要执行任何操作,因为可能还有其他任务等待填充它。

希望对你有帮助!

【讨论】:

因为我将为我的行设置两次标签(一次用于持有者,一次用于整数)我如何指定它来给我整数值? 是的。您还可以考虑几件事。当您处理回收视图(convertView 不为空)时,请考虑在启动新任务之前立即在旧异步任务上调用 cancel() 方法。如果不这样做,在长列表中来回滚动会消耗不必要的 cpu 时间。一个高级但有价值的改进是研究使用 LruCache:developers.google.com/events/io/sessions/gooio2012/103 我的错,它似乎在模拟器上工作,但在设备上却不行。我已经更新了我的代码。我也更新了我的代码。 @NickChris 你有没有解决这个问题?请分享您的解决方案,我遇到了同样的问题。【参考方案2】:

在这种情况下,我的意见总是创建新行。

LayoutInflater inflater = ((Activity)context).getLayoutInflater(); 
row = inflater.inflate(layoutResourceId, parent, false);

不需要使用 Holder(虽然这看起来对平滑有好处)。 试试看。

谢谢。

【讨论】:

以上是关于在列表视图中加载图像时加载错误的图像的主要内容,如果未能解决你的问题,请参考以下文章

将图像从Firebase加载到我的表视图时出错

在 Picasso 图像加载器中加载位图图像会减慢列表视图中的滚动速度

首先显示错误的图像,然后在滚动列表视图期间显示正确的图像

列表视图的 Android LazyList 初始加载在滚动之前不显示图像

为啥偶尔会在 ListView 中加载错误的图像?

Android - 从 mysql 数据库中查看更多旧数据(不是图像),当在列表视图中滚动底部时