ventajas usar studio son recyclerview qué que personalizado los libreria elementos ejemplo cuando construyen cardview android imageview arrays android-recyclerview android-cardview

android - usar - Extraño comportamiento de las imágenes en RecyclerView.



son los elementos qué construyen un recyclerview (3)

Basado en la respuesta de @ Lion789. Estoy usando Volley de Google para cargar imágenes y json.

Esto nos permite recuperar la posición en la vista que se reciclará más adelante.

@Override public int getItemViewType(int position) { return position; }

Al realizar la solicitud, establezca la posición como el TAG

@Override public void onBindViewHolder(final ItemHolder holder, int position) { ImageRequest thumbnailRequest = new ImageRequest(url, new Response.Listener<Bitmap>() { @Override public void onResponse(Bitmap response) { holder.itemThumbnail.setImageBitmap(response); } },[...]); thumbnailRequest.setTag(String.valueOf(position)); //setting the TAG mQueue.addToRequestQueue(thumbnailRequest); }

Y cuando la vista se recicle, cancelaremos la solicitud y eliminaremos la imagen actual.

@Override public void onViewRecycled(ItemHolder holder) { super.onViewRecycled(holder); mQueue.cancelAll(String.valueOf(holder.getItemViewType())); holder.itemThumbnail.setImageDrawable(null); }

Las garantías de volley que cancelan las solicitudes no serán entregadas.

Estoy usando android.support.v7.widget.RecyclerView para mostrar elementos simples que contienen texto y, en algunos casos, también imágenes. Básicamente, seguí el tutorial desde aquí https://developer.android.com/training/material/lists-cards.html y solo cambié el tipo de mDataset de String [] a JSONArray y el xml de ViewHolder. My RecyclerView se encuentra en un fragmento simple:

<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"> <!-- A RecyclerView with some commonly used attributes --> <android.support.v7.widget.RecyclerView android:id="@+id/my_hopps_recycler_view" android:scrollbars="vertical" android:layout_centerInParent="true" android:layout_width="200dp" android:layout_height="wrap_content"/> </RelativeLayout>

En este fragmento estoy cargando toda la información de un servidor web. Después de recibir la información, inicializo los elementos en RecyclerView. Uso un LinearLayoutManager que se configura en el método onCreate de mi Fragmento. Después de agregar el Adaptador, llamo al método setHasFixedSize (true) en mi RecyclerView. Mi clase de adaptador se ve así:

import android.graphics.BitmapFactory; import android.support.v7.widget.RecyclerView; import android.util.Base64; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import org.apache.commons.lang3.StringEscapeUtils; import org.json.JSONArray; import org.json.JSONObject; import saruul.julian.MyFragment; import saruul.julian.R; public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { /** * Fragment holding the RecyclerView. */ private MyFragment myFragment; /** * The data to display. */ private JSONArray mDataset; public static class ViewHolder extends RecyclerView.ViewHolder { public TextView text; ImageView image; public ViewHolder(View v) { super(v); text = (TextView) v.findViewById(R.id.my_view_text); image = (ImageView) v.findViewById(R.id.my_view_image); } } public MyAdapter(MyFragment callingFragment, JSONArray myDataset) { myFragment = callingFragment; mDataset = myDataset; } @Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.my_view, parent, false); ViewHolder vh = new ViewHolder(v); return vh; } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder holder, int position) { try { JSONObject object = mDataset.getJSONObject(position); if ( object.has("image") ){ if ( !hopp.getString("image").equals("") ){ try { byte[] decodedString = Base64.decode(StringEscapeUtils.unescapeJson(object.getString("image")), Base64.DEFAULT); holder.image.setImageBitmap(BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length)); holder.image.setVisibility(View.VISIBLE); } catch ( Exception e ){ e.printStackTrace(); } } } if ( object.has("text") ){ holder.text.setText(object.getString("text")); } } catch ( Exception e ){ e.printStackTrace(); } } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return mDataset.length(); } }

Y mi xml para una vista dentro de mi RecyclerView:

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:id="@+id/card_view" android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content" card_view:cardCornerRadius="4dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" android:padding="@dimen/activity_horizontal_margin"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:id="@+id/my_hopp_people_reached"/> <ImageView android:id="@+id/my_hopp_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="gone" android:adjustViewBounds="true"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:id="@+id/my_hopp_text"/> <ImageButton android:id="@+id/my_hopp_delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:src="@drawable/ic_action_discard" /> </LinearLayout> </android.support.v7.widget.CardView>

Así que ahora aquí está mi problema: todo funciona bien. El texto y la imagen se muestran correctamente en mi RecyclerView. Sin embargo, si llego al final de la lista desplazándome hacia abajo y hacia arriba nuevamente, las imágenes se muestran en elementos que no deben contener imágenes. En este momento tengo nueve artículos. Los dos últimos contienen una imagen. Así que me desplazo hacia abajo y solo veo texto en los primeros siete elementos. Llegar al final de la lista me muestra dos imágenes como se esperaba. Pero si vuelvo a subir esas imágenes aparecen en otros elementos.

El desplazamiento hacia arriba y hacia abajo cambia las imágenes siempre en el mismo orden:

  • El primer desplazamiento hacia arriba (después de haber bajado una vez) crea una imagen en el segundo elemento.
  • El desplazamiento hacia abajo crea una imagen en el séptimo elemento.
  • Desplazar hacia arriba crea una imagen en el primer elemento.
  • El desplazamiento hacia abajo no crea nada.
  • El desplazamiento hacia arriba crea una imagen en el tercer elemento y la imagen en el segundo elemento se sobrescribe con la otra imagen.
  • El desplazamiento hacia abajo crea una imagen en el sexto elemento y deja que la imagen en el séptimo elemento desaparezca.
  • Y así ...

Ya descubrí que el método onBindViewHolder (ViewHolder holder, int position) se llama cada vez que aparece un elemento en la pantalla. Verifiqué la posición y la información dada en mi mDataset. Todo funciona bien aquí: la posición es correcta y la información dada también. El texto en todos los elementos no cambia y siempre se muestra correctamente. ¿Alguien tiene algún tipo de este problema y sabe alguna solución?


Creo que necesita agregar una cláusula else a su if ( object.has("image") ) en el onBindViewHolder(ViewHolder holder, int position) , configurando la imagen en nula:

holder.image.setImageDrawable(null);

Creo que la documentación para onBindViewHolder lo describe mejor por qué esto es necesario:

Llamado por RecyclerView para mostrar los datos en la posición especificada. Este método debe actualizar el contenido del itemView para reflejar el item en la posición dada.

Siempre debe asumir que el ViewHolder que se aprobó necesitaba ser actualizado por completo :)


No estoy seguro si la gente todavía tiene problemas con esto, pero la declaración if no funcionó para mí. Lo que funcionó para mí es esto:

Usando un reemplazo en este método en el adaptador

@Override public int getItemViewType(int position) { return position; }

seguido de llamar a la instrucción if basada en esto para la propia imagen dentro de onBindViewHolder, así:

int pos = getItemViewType(position); if(theList.get(pos).getPicture() == null) { holder.picture.setVisibility(View.GONE); } else { Picasso.with(context).load(R.drawable.robot).into(holder.picture); }

Estoy usando Picasso pero debería funcionar con cualquier otra situación. ¡Espero que esto ayude a alguien!