All images gets loaded in the same place in ListView

75 Views Asked by At

I am having problem in filling an ImageView in my ListView.
The problem is that all the image gets filled in the same place (at the bottom-most list-item's imageview). I can see image getting loaded fast while they gets filled.

public class TheaterListAdapter extends ArrayAdapter<ConstantsTheaterList> implements IResult {

    private ViewHolder viewHolder;

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {


        if (convertView==null){
            convertView = LayoutInflater.from(mContext).inflate(R.layout.fragment_theater_list_item, parent, false);

            viewHolder = new ViewHolder();
            viewHolder.imageView = (ImageView) convertView.findViewById(R.id.theater_list_item_image);
            viewHolder.textViewLatitude = (TextView) convertView.findViewById(R.id.theater_list_item_latitude);
            viewHolder.textViewLongitude = (TextView) convertView.findViewById(R.id.theater_list_item_longitude);
            viewHolder.textViewVicinity = (TextView) convertView.findViewById(R.id.theater_list_item_vicinity);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        //Picasso.with(getContext()).load(uri.toString()).into(mImageView); [WORKS FINE]

        // Using Volley to fetch image from the web.
        new VolleyService(mContext, this).volleyImageRequest(uri.toString(), 100, 100);


        String latitude = getItem(position).latitude;
        String longitude = getItem(position).longitude;
        String vicinity = getItem(position).vicinity;

        viewHolder.textViewLatitude.setText(latitude);
        viewHolder.textViewLongitude.setText(longitude);
        viewHolder.textViewVicinity.setText(vicinity);

        return convertView;
    }

    //These are the callbacks that I receive in my Volley implementation. Now, I fill the bitmap once they are available.
    @Override
    public void notifySuccess(Object response) {
        Bitmap bitmap = (Bitmap) response;
        viewHolder.imageView.setImageDrawable(bitmap);
    }

    @Override
    public void nofifyError(Object volleyError) {
        Log.e(TAG, (String)volleyError);
    }


    private static class ViewHolder{
        ImageView imageView;
        TextView textViewLatitude;
        TextView textViewLongitude;
        TextView textViewVicinity;
    }


}

I believe this must be because of the views being recycled.

However, everything works fine when I use picasso.

How do I fix the same ?

2

There are 2 best solutions below

0
On

For everyone out there, who would still like to understand why this doesn't work and would like to fix it that way, here is how to do it:

Everytime you call your method to fetch your image in background, you need to pass the instance of imageView too where this image is supposed to be placed.

Here is a sample where I've used AsyncTask to fetch the image.

public class TheaterListAdapter extends ArrayAdapter<ConstantsTheaterList> 

    private ViewHolder viewHolder;

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {


        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.fragment_theater_list_item, parent, false);

            viewHolder = new ViewHolder();
            viewHolder.imageView = (ImageView) convertView.findViewById(R.id.theater_list_item_image);
            viewHolder.textViewLatitude = (TextView) convertView.findViewById(R.id.theater_list_item_latitude);
            viewHolder.textViewLongitude = (TextView) convertView.findViewById(R.id.theater_list_item_longitude);
            viewHolder.textViewVicinity = (TextView) convertView.findViewById(R.id.theater_list_item_vicinity);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }


        String key = mContext.getResources().getString(R.string.google_maps_key);
        String photoReference = getItem(position).photo_reference;
        String maxWidth = getItem(position).max_width;
        String maxHeight = getItem(position).max_height;
        String url = "https://maps.googleapis.com/maps/api/place/photo?";

        Uri uri = Uri.parse(url).buildUpon()
                .appendQueryParameter("key", key)
                .appendQueryParameter("maxwidth", maxWidth)
                .appendQueryParameter("maxheight", maxHeight)
                .appendQueryParameter("photoreference", photoReference)
                .build();

        //Picasso.with(getContext()).load(uri.toString()).into(mImageView);

        //new VolleyService(mContext, this).volleyImageRequest(uri.toString(), 100, 100);

        // Preparing input for MyAsyncTask...
        Object[]  object = new Object[2];
        object[0] = uri.toString();
        object[1] = viewHolder.imageView; // This is **important**
                                          //Pass the instance of imageview where the image is supposed to be filled.

        //Calling AsyncTask...
        MyAsyncTask myAsyncTask = new MyAsyncTask();
        myAsyncTask.execute(object);


        String latitude = getItem(position).latitude;
        String longitude = getItem(position).longitude;
        String vicinity = getItem(position).vicinity;

        viewHolder.textViewLatitude.setText(latitude);
        viewHolder.textViewLongitude.setText(longitude);
        viewHolder.textViewVicinity.setText(vicinity);

        return convertView;
    }
    private static class ViewHolder {
        ImageView imageView;
        TextView textViewLatitude;
        TextView textViewLongitude;
        TextView textViewVicinity;
    }

    private static class MyAsyncTask extends AsyncTask<Object, Void, Bitmap>{

        private String url;
        private ImageView imageView;
        @Override
        protected Bitmap doInBackground(Object... params) {

            url = (String) params[0];
            Log.v(TAG, url);
            imageView = (ImageView) params[1];

            return Utility.ImageHttp.read(url); // my method to fetch the image and return the Bitmap. 
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            imageView.setImageBitmap(bitmap);
        }
    }
}

Now that you have understood, you can use Picasso for this thing. But it's important to understand why it didn't work in the first place.

1
On

The reason your facing this issue is that, you are not using the View holder properly.
You see, view holder has it's instance associated with each cell. Here, when you call method of "Volley" to download the pic and then through it's callback method you populate the ImageView, using "ViewHolders" instance, that instance is somehow associated with last cell of the ListView.

There is also one more problem, that you are not passing the instance of the cell for which you are downloading the pic. The instance is misplaced.

You can use Picasso for this or UniversalImageLoader. As the these libraries allow you to pass the instance of the imageview of that particular cell for which you are downloading the image.
Example:

Picasso.with(this).load("YOUR IMAGE URL HERE").into(ViewHolder.imageView);
That's it. Your good to go...