Images loaded in ListView are randomly exchanged

1

I've implemented a ListView where an external image is populated in each position, but when I scroll on the screen the images change randomly.

This video shows exactly what happens: link

Note: There should be no duplicate images in the ListView.

DownloadTask:

public class DownloadTask extends AsyncTask<String, Void, Bitmap> {
    private final WeakReference<ImageView> imageView;

    public DownloadTask(ImageView thumbnail)
    {
        imageView = new WeakReference<ImageView>(thumbnail);
    }

    @Override
    protected Bitmap doInBackground(String... params)
    {

        try {
            InputStream strem = new URL(params[0]).openConnection().getInputStream();
            return BitmapFactory.decodeStream(strem);
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return null;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap)
    {
        ImageView thumbnail = imageView.get();

        if (thumbnail != null)
        {
            if (bitmap != null)
            {
                thumbnail.setImageBitmap(bitmap);
            }
        }
    }
}

Adapter:

public class Adapter extends SimpleAdapter {
    public LayoutInflater inflater = null;

    public Adapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)
    {
        super(context, data, resource, from, to);

        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(int position, View view, ViewGroup parent) {
        ViewHolder viewHolder;

        HashMap<String, Object> data = (HashMap<String, Object>) super.getItem(position);

        if(view == null){
            view = inflater.inflate(R.layout.clientes, parent, false);

            viewHolder = new ViewHolder();

            viewHolder.thumbnail = (ImageView) view.findViewById(R.id.thumbnail);
            viewHolder.title = (TextView)view.findViewById(R.id.title);
            viewHolder.content = (TextView)view.findViewById(R.id.content);

            view.setTag(viewHolder);
        }else{
            viewHolder = (ViewHolder)view.getTag();
        }

        new DownloadTask(viewHolder.thumbnail).execute(data.get("thumbnail").toString());

        viewHolder.title.setText(data.get("title").toString());
        viewHolder.content.setText(data.get("content").toString());

        return view;
    }

    static class ViewHolder{
        ImageView thumbnail;
        TextView title;
        TextView content;
    }
}
    
asked by anonymous 22.07.2015 / 17:44

1 answer

5

Rafael, this problem is expected by the way it was implemented.

You are not doing any processing to verify that at the end of the download, that image should still be shown for that ImageView.

This is due to the ListView's behavior of reserving a fixed amount of Views to be reused. So you can have two Tasks with the same reference for a given ImageView.

To solve, there are two ways:

  • Use a lib to upload images, and there are dozens of Picasso, Glide, Universal Image Loader, Volley, Fresco itself and etc ...

    I think it's the best alternative, you do not have to reinvent the wheel, because you can even handle caching and many other things, which is valuable in the mobile world. And these libs have an algorithm for this kind of recycling treatment of ListView.

  • If it is to do with Task, I recommend checking the View with some Task key, because at the end of Download you can check this key and compare it with the current Task key, the normal Bitmap, otherwise ignore.

22.07.2015 / 18:52