Un enfoque alternativo para mostrar la carga durante la paginación

imagen

Trabajar con la carga de página por página en aplicaciones móviles es un tema bastante simple y no tiene inconvenientes.

El desarrollador se enfrenta a una tarea similar con bastante frecuencia y, en consecuencia, una y otra vez hacer lo mismo es aburrido y perezoso.

Pero cualquier tarea, incluso la más común, puede resolverse de otra manera más interesante, para obtener nuevos conocimientos y simplemente estirar el cerebro.

Paginación


En resumen, todo el trabajo sobre la implementación de la paginación consta de los elementos que se enumeran a continuación:

  1. Agregar oyente a la vista de reciclador;
  2. Descargar datos primarios;
  3. Captura una devolución de llamada cuando el usuario se desplaza por la lista;
  4. Mostrar descarga en la lista después de todos los elementos;
  5. Enviar una solicitud de nuevos artículos;
  6. Mostrar datos nuevamente.

En nuestro caso, se implementó el mecanismo de paginación de página, pero no se mostró la carga mientras el usuario se desplazaba por la lista.

Bien, hacer esto una vez en una aplicación no es un problema. Si hay varias pantallas donde se necesita esta funcionalidad, puede escribir un adaptador básico que pueda funcionar con un tipo de vista adicional.

Pero, ¿qué pasa si la tarea es algo más complicada?

Digamos que hay varios proyectos no nuevos que tienen un módulo central con funcionalidad básica.

Como los proyectos están lejos del primer año, como resultado, se ha acumulado una gran cantidad de código heredado relacionado con esta tarea, es decir, muchos adaptadores con diferentes clases base.

¿Cómo puedo resolver el problema?


Cree un nuevo adaptador base que pueda mostrar el progreso de la descarga, reescriba todos los adaptadores + pantallas anteriores que usan los adaptadores, ya que debe controlar el estado de visualización de la carga en cada pantalla, en cada presentador o vista.

Esto puede llevar bastante tiempo. Y también una refactorización grande puede conducir a una serie de nuevos errores, especialmente si la cobertura de prueba en los proyectos tiende a cero. Además, una gran carga en el departamento de control de calidad con pruebas de regresión de todas las pantallas con paginación para varias aplicaciones.

Sería genial escribir un código que requiera un tiempo mínimo del cliente de desarrollo para integrar la solución básica en su función. Para que no tenga que implementar la lógica de control de mostrar el progreso de la carga: mostrar y ocultar.

En este punto, pensé en usar la clase ItemDecoration .

¿Por qué no utilizar esta clase para encapsular todo el trabajo relacionado con

  • determinar cuándo mostrar la carga y cuándo ocultarla
  • progreso de descarga de dibujo

Desafortunadamente, no se encontró una sola solución preparada en Internet, ya sea que nadie intentó implementarla o simplemente no compartió la experiencia después de la implementación.

¿Qué se necesita para implementar la visualización de carga durante la paginación usando ItemDecoration?


  • Sangría para representar la carga;
  • Comprender la implementación de ItemDecoration cuando necesitamos mostrar progreso;
  • Descarga de sorteos;

Sangría


Cualquier desarrollador que haya creado SpacingItemDecoration al menos una vez sabe cómo sangrar. El método itemDecoration getItemOffsets nos ayudará con esto:

override fun getItemOffsets(outRect: Rect, view: View, recyclerView: RecyclerView, state: RecyclerView.State) { super.getItemOffsets(outRect, view, parent, state) } 

Para cualquier elemento de la lista, podemos agregar sangrías a cada lado. Específicamente, en nuestro caso, debemos entender que el usuario ha desplazado la lista hasta el final y en el último elemento de sangría desde la parte inferior igual al valor del progreso de descarga visualizado en el futuro + sangrías del progreso mismo.

¿Cómo entender que la lista se desplaza hasta el final?

El siguiente código nos ayudará con esto:

  private fun isLastItem(recyclerView: RecyclerView, view: View): Boolean { val lastItemPos = recyclerView.getChildAdapterPosition(view) return lastItemPos == recyclerView.adapter!!.itemCount - 1 } 

En total, tenemos código para definir e implementar sangría:

  override fun getItemOffsets(outRect: Rect, view: View, recyclerView: RecyclerView, state: RecyclerView.State) { super.getItemOffsets(outRect, view, recyclerView, state) when (isLastItem(recyclerView, view)) { true -> outRect.set(Rect(0, 0, 0, 120)) else -> outRect.set(Rect(0, 0, 0, 0)) } } private fun isLastItem(recyclerView: RecyclerView, view: View): Boolean { val lastItemPos = recyclerView.getChildAdapterPosition(view) return lastItemPos == recyclerView.adapter!!.itemCount - 1 } 

¡Un tercio del trabajo está hecho!

Determinamos el tiempo de visualización del progreso de la descarga y llamamos a la representación del progreso


El método ItemDecoration onDrawOver nos ayudará con esto:

  override fun onDrawOver(canvas: Canvas, recyclerView: RecyclerView, state: RecyclerView.State) { super.onDrawOver(canvas, recyclerView, state) } 

El método onDrawOver difiere del método onDraw solo en el orden de representación. Las decoraciones de OnDrawOver solo se procesarán después de que se haya dibujado el elemento de la lista.

En el método onDrawOver, debemos comprender en qué punto necesitamos dibujar el progreso, en qué punto eliminarlo y generar actualizaciones en los elementos de la lista para ocultar el sangrado ya sangrado.

Código para implementar las acciones descritas anteriormente:

  override fun onDrawOver(canvas: Canvas, recyclerView: RecyclerView, state: RecyclerView.State) { super.onDrawOver(canvas, recyclerView, state) when (showLoading(recyclerView)) { true -> { PaginationProgressDrawer.drawSpinner(recyclerView, canvas) isProgressVisible = true } else -> { if (isProgressVisible) { isProgressVisible = false recyclerView.invalidateItemDecorations() } } } } private fun showLoading(recyclerView: RecyclerView): Boolean { val manager = recyclerView.layoutManager as LinearLayoutManager val lastVisibleItemPos = manager.findLastCompletelyVisibleItemPosition() return lastVisibleItemPos != -1 && lastVisibleItemPos >= recyclerView.adapter!!.itemCount - 1 } 

Progreso del dibujo


El código de representación es bastante voluminoso y se presentará en un archivo separado, un enlace al cual presentaré a continuación.

Quiero detenerme solo en los matices que son necesarios para la implementación.

Todo el trabajo de dibujo se realiza en lienzo. En consecuencia, será necesario configurar una instancia del objeto Paint y dibujar arcos, indicando los ángulos inicial y final.

Un matiz importante es que para renderizar tanto como sea posible de manera similar a un ProgressBar regular, debe crear su propio análogo de un interpolador para que la animación de renderizado ocurra de forma no lineal.

Para comprender el trabajo de los interpoladores y comprender lo que desea obtener de su animación, le recomiendo que se familiarice con los gráficos que representan el trabajo de varios tipos de interpoladores, por ejemplo, en este artículo.

Referencias de código:

PaginationLoadingDecoration
PaginationProgressDrawer

Si es necesario, puede crear una interfaz ProgressDrawer y reemplazar implementaciones en PaginationLoadingDecoration.

Descargar video de demostración:


Gracias por leer, disfruta codificando :)

Source: https://habr.com/ru/post/468877/


All Articles