El siguiente es el código del adaptador que estoy usando para mostrar elementos de película que contienen Imagen de película y Título de película.

   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);


   }
}
}

Mientras se desplazan las imágenes hacia abajo, se están cargando imágenes de otras películas. Por ejemplo, para una película, se muestra otra imagen de película y después de un segundo también se muestra otra imagen de película y de esa manera después de una serie de imágenes diferentes la imagen correcta se está mostrando

Estoy descargando la imagen de la URL que obtengo de la respuesta en el métodoBindViewHolder. Cómo resolver este problema?

2
Aravind Pulagam 23 feb. 2018 a las 18:59

4 respuestas

La mejor respuesta

Problema

Lo importante aquí es darse cuenta de que los ViewHolders son un grupo de objetos reutilizados y, a medida que se desplaza, aparecerá un ViewHolder reutilizado y se mostrará la imagen cargada previamente en ella.

Al usar Picasso o Glide o similar, está ahorrando una increíble cantidad de código que también usa características adicionales como caché y efectos.

Solución

Primero puedes usar una biblioteca como Picasso

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

Luego en su vista de reciclador en el visor de enlace

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

La primera línea de código detendrá la carga de cualquier imagen anterior. La segunda línea cargará la imagen actual.

Reemplace "http://image.com/image.png" con la url de su imagen. Se necesita cadena, URL, etc.

Lea más sobre Picasso aquí

4
toshkinl 23 feb. 2018 a las 16:15

Problema

La causa raíz de su problema es que el mapa de bits se recupera después de que la vista ha sido reciclada y, por lo tanto, obtiene una falta de coincidencia cuando la usa.

Cómo resolver

  • La respuesta corta es utilizar una biblioteca de almacenamiento en caché de imágenes como Picasso o Glide. Estas bibliotecas fueron diseñadas para hacer exactamente lo que está tratando de lograr.
  • Otra opción es intentar hacer esto por su cuenta. Lo que puede hacer es guardar la URL dentro del titular y llamar a setImageBitmap() solo en caso de que la URL actual coincida con la que acaba de obtener. Para guardar la URL, simplemente agregue un campo de URL en MovieViewHolder y almacene la URL cada vez que recupere la imagen y verifique que coincida cuando termine de descargar.
1
Doron Yakovlev-Golani 23 feb. 2018 a las 19:32

También busqué esta solución y, a partir de la respuesta de Doron Yakovlev-Golani, hice una solución propia, ya que mi reputación es baja, por lo que no puedo agregar un comentario. A continuación se muestra mi solución, aquí, cuando agregué una imagen en ImageView, también agregué una ContentDescription a ImageView como referencia para uso futuro para determinar qué imagen se debe mostrar cuando se llama a ViewHolder. ContentDescription es la posición actual de ListView como String. Aquí hay dos condiciones, la primera para verificar la imagen agregada previamente o no, si la imagen no se agregó previamente, agregué la imagen. segundo una imagen ya agregada en ImageView, así que compruebo qué imagen necesita cargar llamando a ImageView ContentDescription, si ContentDescription coincide con la posición actual significa que necesito agregar imágenes de posiciones actuales a ImageView desde mi lista.

En su onViewHolder puede usar así:

//checking it is first time or not
    if (holder.mv_img.getContentDescription() == null) {
        //adding current position for loading current position image
        imageView.setContentDescription(position + "");
    }
    if (holder.mv_img.getContentDescription().equals(position + "")) {
        holder.mv_img.setImageBitmap(userLogoUri);
    }
0
Rhidoy 6 sep. 2018 a las 14:21

Utilice Picasso o Biblioteca Glide para mostrar imágenes en RecyclerView

1
Ramesh R 23 jul. 2019 a las 13:03