RecyclerView StaggeredGridLayoutManager problema de reordenación
Estoy intentando exhibir tres (por lo menos ése es el caso que tengo un problema con) artículos en un RecyclerView
con un StaggeredGridLayoutManager
con dos columnas. El primer elemento se distribuye entre las dos filas. Así es como se ve:
- RecyclerView findViewHolder null para elementos fuera de pantalla
- El mejor lugar para llamar a RecyclerView.Adapter.notifyItem *?
- Uso de JsonArray vs ArrayList como conjunto de datos para RecyclerView Adapter
- Actualizaciones de datos de RecyclerView y Adaptador
- Cómo pasar valores de RecycleAdapter a MainActivity u Otras actividades
Ahora, voy a mover el elemento "Artículo 2" a la parte superior. Aquí está el código que llamo, en el adaptador (es una muestra que escribí para demostrar el problema que tengo en un proyecto más complejo):
private int findById(int id) { for (int i = 0; i < items.size(); ++i) { if (items.get(i).title.equals("Item " + id)) { return i; } } return -1; } // Moving the item "Item 2" with id = 2 and position = 0 public void moveItem(int id, int position) { final int idx = findById(id); final Item item = items.get(idx); if (position != idx) { items.remove(idx); items.add(position, item); notifyItemMoved(idx, position); //notifyDataSetChanged(); } }
Después de eso, la matriz está bien: [Item 2, Item 1, Item 3]
. Sin embargo, la vista está lejos de estar bien:
Si toco el RecyclerView
(lo suficiente como para activar el efecto de overscroll si no hay suficientes elementos para desplazarse), el elemento 2 se mueve a la izquierda, donde esperaba verlo en primer lugar (con una animación agradable):
Como usted vio quizá en el código, intenté substituir notifyItemMoved(idx, position)
por una llamada a notifyDataSetChanged()
. Funciona, pero el cambio no está animado.
Escribí una muestra completa para demostrar esto y ponerlo en GitHub . Es casi mínimo (hay opciones para mover el elemento y cambiar su alcance).
No veo lo que puedo estar haciendo mal. ¿Se trata de un error con StaggeredGridLayoutManager
? Me gustaría evitar notifyDataSetChanged()
como me gustaría mantener la coherencia con respecto a las animaciones.
Editar: después de un poco de excavación, no hay necesidad de un elemento completo para mostrar el problema. Quité el span completo. Cuando trato de mover el elemento 2 a la posición 0, no se mueve : el elemento 1 va tras él y el elemento 3 se mueve a la derecha, así que tengo: celda vacía, elemento 2, nueva línea, elemento 1, elemento 3 Todavía tengo el diseño correcto después de un rollo.
Lo que es más interesante es que no tengo el problema con un GridLayoutManager
. Necesito un artículo de pleno-span por lo que no es una solución, pero supongo que es de hecho un error en el StaggeredGridLayoutManager
…
- Color de fondo o imágenes barajando en desplazamiento en recyclerview?
- RecyclerView imágenes desaparecidas
- RecyclerView actualiza sólo en desplazamiento
- ¿Cómo guardar el estado del elemento de la fila RecyclerView?
- Equivalente de ListView.setEmptyView en RecyclerView
- No se puede llamar al método notifyItemInserted en una devolución de llamada de desplazamiento recyclerview-v7: 24.2.0
- NotifyItemChanged (int position) actualiza varios elementos en RecyclerView
- RecyclerView: cómo borrar las vistas en caché / reciclado?
No tengo una respuesta completa, pero puedo señalarles tanto una solución como el informe de errores (que creo que está relacionado).
El truco para actualizar el diseño para que se vea como su segunda captura de pantalla, es llamar invalidateSpanAssignments()
en el StaggeredGridLayoutManger
(sglm) después de haber llamado notifyItemMoved()
. El "desafío" es que si lo llama inmediatamente después de nIM()
, no se ejecutará. Si retrasa la llamada por unos pocos ms, lo hará. Por lo tanto, en su código referenciado para MainActivity, he hecho su sglm
un campo privado:
private StaggeredGridLayoutManager sglm; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); adapter = new Adapter(); recyclerView = (RecyclerView) findViewById(R.id.recycler_view); sglm = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(sglm); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(adapter); }
Y abajo en el bloque de interruptores, haga referencia a él en un controlador:
case R.id.move_sec_top: adapter.moveItem(2, 0); new Handler().postDelayed(new Runnable() { @Override public void run() { sglm.invalidateSpanAssignments(); } }, 100); return true;
El resultado es que su animación sigue funcionando, el diseño termina de la manera que lo desea. Esto es un verdadero problema, pero funciona. Creo que este es el mismo error que encontré e informé en el siguiente enlace:
https://code.google.com/p/android/issues/detail?id=93156
Aunque mi "síntoma" y la llamada requerida eran diferentes, la cuestión subyacente parece ser idéntica.
¡Buena suerte!
EDIT: No hay necesidad de postDelayed , simplemente publicar hará el truco:
case R.id.move_sec_top: adapter.moveItem(2, 0); new Handler().post(new Runnable() { @Override public void run() { sglm.invalidateSpanAssignments(); } }); return true;
Mi teoría original era que la llamada fue bloqueada hasta que el pase de la disposición fuera encima, pero creo que no es el caso. En su lugar, ahora creo que si llama invalidateSpanAssignments()
inmediatamente, en realidad se ejecuta demasiado pronto (antes de que los cambios de diseño hayan terminado). Por lo tanto, el mensaje anterior (sin demora) simplemente agrega la llamada al final de la cola de renderizado donde sucede después del diseño.
Bueno, yo he hecho de esta manera.
StaggeredGridLayoutManager gaggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); gaggeredGridLayoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); recyclerView.setLayoutManager(gaggeredGridLayoutManager); dataList = YourDataList (Your Code for Arraylist); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerAdapter = new DataAdapter(dataList, recyclerView); recyclerView.setAdapter(recyclerAdapter); // Magic line recyclerView.addOnScrollListener(new ScrollListener());
Cree una clase para el RecyclerView Scroll Listener personalizado .
private class ScrollListener extends RecyclerView.OnScrollListener { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { gaggeredGridLayoutManager.invalidateSpanAssignments(); } }
Espero que esto te ayudará.
- Recurso para descargar temas / estilos de aplicación de Android?
- Android BOOT_COMPLETED no se recibe cuando se cierra la aplicación