androidandroid-recyclerviewandroid-networking

RecyclerView displaying wrong Images


The following is the code for the adapter I'm using to display movie items that contain Movie Images and Movie Titles.

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {

    private List<Movie> movieList;

    public MovieAdapter(List<Movie> movieList) {
        this.movieList = movieList;
    }

    @Override
    public MovieViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.movie_item, parent, false);
        return new MovieViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final MovieViewHolder holder, int position) {
        Movie movie = movieList.get(position);
        holder.mv_name.setText(movie.getName());
        class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {
            @Override
            protected Bitmap doInBackground(String... strings) {
                Bitmap bitmap = null;
                URL url = createUrl(strings[0]);
                Log.v("stringurl", strings[0]);
                try {
                    bitmap = makeHttpRequest(url);

                } catch (IOException e) {
                }
                return bitmap;
            }

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

            private URL createUrl(String StringUrl) {
                URL url = null;

                try {
                    url = new URL(StringUrl);
                } catch (MalformedURLException e) {
                    return null;
                }

                return url;
            }

            private Bitmap makeHttpRequest(URL url) throws IOException {
                HttpURLConnection urlConnection = null;
                InputStream stream = null;
                Bitmap bitmap = null;
                try {
                    urlConnection = (HttpURLConnection) url.openConnection();
                    urlConnection.setRequestMethod("GET");
                    urlConnection.setConnectTimeout(10000);
                    urlConnection.setReadTimeout(15000);
                    urlConnection.connect();

                    stream = urlConnection.getInputStream();
                    bitmap = BitmapFactory.decodeStream(stream);
                    //Log.v("image:",bitmap.toString());
                    return bitmap;
                } catch (IOException e) {

                } finally {
                    if (urlConnection != null) urlConnection.disconnect();
                    if (stream != null) stream.close();
                }
                return bitmap;
            }
        }
        new ImageDownloadTask().execute(movie.getImg());
    }

    @Override
    public int getItemCount() {
        return movieList == null ? 0 : movieList.size();
    }

    class MovieViewHolder extends RecyclerView.ViewHolder {
        ImageView mv_img;
        TextView mv_name;

        public MovieViewHolder(View itemView) {
            super(itemView);
            mv_img = (ImageView) itemView.findViewById(R.id.mv_img);
            mv_name = (TextView) itemView.findViewById(R.id.mv_name);
        }
    }
}

While scrolling down images are loading images of other movies are displaying. For example, for a movie, another movie image is getting displayed and after a second also another movie image is getting displayed in that way after a series of different images correct image is getting displayed.

I'm downloading images from the Url I get from the response inBindViewHolder method. How to resolve this issue?


Solution

  • Problem

    The thing here is to realize that the ViewHolders are pool of reused objects and as you scroll a reused ViewHolder will appear and the previously loaded in it image will be shown.

    By using Picasso or Glide or similar you are saving incredible amount of code also using additional features like cache and effects.

    Solution

    First you can use a library like Picasso

    compile 'com.squareup.picasso:picasso:2.5.2'

    then in your recyclerview on bind viewholder

    Picasso.with(holder.imageView.getContext()).cancelRequest(holder.imageView);
    Picasso.with(holder.imageView.getContext()).load("http://image.com/image.png").into(holder.imageView);
    

    The first line of code will stop any previous images from loading. The second line will load the current image.

    Replace "http://image.com/image.png" with your image url. It takes String, URL, etc...

    Read more about Picasso here