RecyclerView con GridLayoutManager y Picasso mostrando imagen incorrecta
Actualización # 1
Añadido hasStableIds (true) y Picasso actualizado a la versión 2.5.2. No resuelve el problema.
- Haga doble clic en OnClickListener de un adaptador de vista de reciclador
- AppBarLayout no siempre vuelve a entrar en desplazamiento hacia abajo
- Agregar nuevo elemento a la parte superior del RecyclerView
- Diseño cuadrado en GridLayoutManager para RecyclerView
- ¿Cómo manejar los contadores de cuenta regresiva múltiples en RecyclerView?
Reproducción:
RecyclerView con GridLayoutManager (spanCount = 3). Los elementos de lista son CardViews con ImageView dentro.
Cuando todos los elementos no se ajustan a la llamada de la pantalla notifyItemChanged en un elemento causa más de una llamada a onBindViewHolder (). Una llamada es para la posición de notifyItemChanged otros para los elementos no visibles en la pantalla.
Problema:
A veces el elemento en la posición pasada a la notifyItemChanged se carga con una imagen que pertenece a un elemento que no está en la pantalla (muy probablemente debido al reciclado del titular de la vista – aunque yo asumiría que si el elemento permanece en su lugar, Sería el mismo).
He encontrado el comentario de Jake sobre otro tema aquí sobre la llamada load () incluso si el archivo / uri es nulo. La imagen se carga en cada onBindViewHolder aquí.
Ejemplo de aplicación simple:
git clone https://github.com/gswierczynski/recycler-view-grid-layout-with-picasso.git
Toque en un elemento llama a notifyItemChanged con un parámetro igual a la posición de ese elemento.
Código:
public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(R.id.container, new PlaceholderFragment()) .commit(); } } public static class PlaceholderFragment extends Fragment { public PlaceholderFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); RecyclerView rv = (RecyclerView) rootView.findViewById(R.id.rv); rv.setLayoutManager(new GridLayoutManager(getActivity(), 3)); rv.setItemAnimator(new DefaultItemAnimator()); rv.setAdapter(new ImageAdapter()); return rootView; } } private static class ImageAdapter extends RecyclerView.Adapter<ImageViewHolder> implements ClickableViewHolder.OnClickListener { public static final String TAG = "ImageAdapter"; List<Integer> resourceIds = Arrays.asList( R.drawable.a0, R.drawable.a1, R.drawable.a2, R.drawable.a3, R.drawable.a4, R.drawable.a5, R.drawable.a6, R.drawable.a7, R.drawable.a8, R.drawable.a9, R.drawable.a10, R.drawable.a11, R.drawable.a12, R.drawable.a13, R.drawable.a14, R.drawable.a15, R.drawable.a16, R.drawable.a17, R.drawable.a18, R.drawable.a19, R.drawable.a20); @Override public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false); return new ImageViewHolder(v, this); } @Override public void onBindViewHolder(ImageViewHolder holder, int position) { Log.d(TAG, "onBindViewHolder position: " + position + " | holder obj:" + holder.toString()); Picasso.with(holder.iv.getContext()) .load(resourceIds.get(position)) .fit() .centerInside() .into(holder.iv); } @Override public int getItemCount() { return resourceIds.size(); } @Override public void onClick(View view, int position) { Log.d(TAG, "onClick position: " + position); notifyItemChanged(position); } @Override public boolean onLongClick(View view, int position) { return false; } } private static class ImageViewHolder extends ClickableViewHolder { public ImageView iv; public ImageViewHolder(View itemView, OnClickListener onClickListener) { super(itemView, onClickListener); iv = (ImageView) itemView.findViewById(R.id.iv); } } } public class ClickableViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { OnClickListener onClickListener; public ClickableViewHolder(View itemView, OnClickListener onClickListener) { super(itemView); this.onClickListener = onClickListener; itemView.setOnClickListener(this); itemView.setOnLongClickListener(this); } @Override public void onClick(View view) { onClickListener.onClick(view, getPosition()); } @Override public boolean onLongClick(View view) { return onClickListener.onLongClick(view, getPosition()); } public static interface OnClickListener { void onClick(View view, int position); boolean onLongClick(View view, int position); } }
- RecyclerView desplazamiento horizontal desplazamiento en el centro
- Android Visualización de datos de json a cardview en RecyclerView
- Cómo centrar RecyclerView elementos horizontalmente con vertical GridLayoutManager
- GridLayoutManager 3 columnas en RecyclerView
- Cambiar el tamaño de una vista en OnLayoutChangeListener
- "RecyclerView: No hay adaptador conectado; Omitir el diseño "para recyclerview en el fragmento
- Implementar el gesto de deslizamiento en un elemento de RecyclerView?
- Efecto Parallax en cada elemento en una vista de reciclador?
Pasé más tiempo del que me gustaría admitir para trabajar con rarezas con RecyclerView
y el nuevo adaptador que viene con él. Lo único que finalmente funcionó para mí en términos de actualizaciones correctas y asegurarse de que notifyDataSetChanges
y todos sus otros hermanos no causaron un comportamiento extraño fue el siguiente:
En mi adaptador,
setHasStableIds(true);
En el constructor. Entonces anulé este método:
@Override public long getItemId(int position) { // return a unique id here }
Y se aseguró de que todos mis artículos devolvieran un identificador único.
Cómo lograrlo depende de usted. Para mí, los datos fueron suministrados desde mi servicio web en la forma de un UUID y engañé convirtiendo partes del UUID a largo usando esto:
SomeContent content = _data.get(position); Long code = Math.abs(content.getContentId().getLeastSignificantBits());
Obviamente esto no es un enfoque muy seguro, pero es probable que funcione para mis listas que contendrán <1000 elementos. Hasta ahora no he tenido ningún problema con él.
Lo que recomiendo es probar este enfoque y ver si funciona para usted. Puesto que usted tiene una matriz, obtener un número único para usted debe ser simple. Tal vez trate de devolver la posición del elemento real ( y no la posición que se pasa en el getItemId()
) o crear un único largo para cada uno de sus registros y pasar que en.
¿Ha intentado llamar al método mutate mutate()
en el Drawable? Véase aquí , por ejemplo.
Aquí una solución de trabajo pero tiene problemas gráficos al llamar a notifyDataSetChanged()
holder.iv.post(new Runnable() { @Override public void run() { Picasso.with(holder.iv.getContext()) .load(resourceIds.get(position)) .resize(holder.iv.getWidth(), 0) .into(holder.iv); });
Funciona porque en este punto la imagen tiene un ancho, por desgracia cuando necesito actualizar todas las casillas de verificación en el visor (como una acción select all), y llamo notifyDataSetChanged()
y el efecto es muy feo
Sigue buscando una mejor solución
Editar: esta solución funciona para mí:
holder.iv.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) holder.iv.getViewTreeObserver().removeOnGlobalLayoutListener(this); else holder.iv.getViewTreeObserver().removeGlobalOnLayoutListener(this); Picasso.with(holder.iv.getContext()) .load(resourceIds.get(position)) .resize(holder.iv.getMeasuredWidth(), 0) .into(holder.iv); } });
- Android: las esquinas de forma no funcionan al configurar las esquinas individuales
- Emita el desplazamiento WebView en NestedScrollView Android 2.3 o menos