Ein alternativer Ansatz zum Anzeigen der Last während der Paginierung

Bild

Das Arbeiten mit dem seitenweisen Laden in mobilen Anwendungen ist ein recht einfaches Thema und weist keine Fallstricke auf.

Der Entwickler steht ziemlich oft vor einer ähnlichen Aufgabe und dementsprechend ist es langweilig und faul, immer wieder dasselbe zu tun.

Aber jede gewöhnliche Aufgabe kann auf eine andere, interessantere Weise gelöst werden, um neues Wissen zu erlangen und einfach Ihr Gehirn zu dehnen.

Paginierung


Kurz gesagt, alle Arbeiten zur Implementierung der Paginierung bestehen aus den nachstehend aufgeführten Punkten:

  1. Listener zur Recycler-Ansicht hinzufügen;
  2. Primärdaten herunterladen;
  3. Erhalten Sie einen Rückruf, wenn der Benutzer durch die Liste blättert.
  4. Download in der Liste nach allen Elementen anzeigen;
  5. Senden Sie eine Anfrage für neue Artikel;
  6. Daten erneut anzeigen.

In unserem Fall wurde der Seiten-Paging-Mechanismus implementiert, aber die Last wurde nicht angezeigt, während der Benutzer durch die Liste blätterte.

Okay, dies einmal in einer Anwendung zu tun, ist kein Problem. Wenn es mehrere Bildschirme gibt, auf denen diese Funktionalität benötigt wird, können Sie einen Basisadapter schreiben, der mit einem zusätzlichen Ansichtstyp arbeiten kann.

Was aber, wenn die Aufgabe etwas komplizierter ist?

Angenommen, es gibt mehrere nicht neue Projekte mit einem Kernmodul mit grundlegenden Funktionen.

Da Projekte weit vom ersten Jahr entfernt sind, wurde im Zusammenhang mit dieser Aufgabe viel Legacy-Code angesammelt, nämlich viele Adapter mit unterschiedlichen Basisklassen.

Wie kann ich das Problem lösen?


Erstellen Sie einen neuen Basisadapter, der den Fortschritt des Downloads anzeigen kann, und schreiben Sie alle alten Adapter + Bildschirme neu, die die Adapter verwenden, da Sie den Anzeigezustand der Last auf jedem Bildschirm, in jedem Präsentator oder in jeder Ansicht steuern müssen.

Dies kann sehr zeitaufwändig sein. Und auch großes Refactoring kann zu einer Reihe neuer Fehler führen, insbesondere wenn die Testabdeckung in Projekten gegen Null geht. Plus, eine hohe Belastung für die QS-Abteilung mit Regressionstests aller Bildschirme mit Paginierung für mehrere Anwendungen.

Es wäre großartig, solchen Code zu schreiben, der vom Entwicklungsclient eine minimale Zeit benötigt, um die Basislösung in seine Funktion zu integrieren. Um nicht die Steuerlogik der Anzeige des Ladevorgangs implementieren zu müssen - Ein- und Ausblenden.

Zu diesem Zeitpunkt habe ich über die Verwendung der ItemDecoration- Klasse nachgedacht .

Verwenden Sie diese Klasse, um alle Arbeiten zu kapseln, die sich auf diese Klasse beziehen

  • Bestimmen, wann die Last angezeigt und wann sie ausgeblendet werden soll
  • Zeichnungs-Download-Fortschritt

Leider wurde im Internet keine einzige vorgefertigte Lösung gefunden - entweder hat niemand versucht, sie zu implementieren, oder er hat nach der Implementierung einfach keine Erfahrungen ausgetauscht.

Was ist erforderlich, um die Ladeanzeige während der Paginierung mit ItemDecoration zu implementieren?


  • Einzug, um die Last zu rendern;
  • Verstehen Sie die Implementierung von ItemDecoration, wenn wir Fortschritte zeigen müssen.
  • Download herunterladen;

Einrückung


Jeder Entwickler, der SpacingItemDecoration mindestens einmal erstellt hat, weiß, wie man einrückt. Die Methode getItemOffsets von ItemDecoration hilft uns dabei:

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

Für jedes Element der Liste können wir auf beiden Seiten Einrückungen hinzufügen. In unserem Fall müssen wir insbesondere verstehen, dass der Benutzer die Liste bis zum Ende gerollt hat und beim letzten Elementeinzug von unten dem Wert des zukünftigen angezeigten Download-Fortschritts + Einrückungen vom Fortschritt selbst entspricht.

Wie kann man verstehen, dass die Liste nach unten gescrollt wird?

Der folgende Code hilft uns dabei:

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

Insgesamt haben wir Code zum Definieren und Implementieren von Einrückungen:

  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 } 

Ein Drittel der Arbeit ist erledigt!

Wir bestimmen den Zeitpunkt der Anzeige des Fortschritts des Downloads und nennen das Rendern des Fortschritts


Die ItemDecoration onDrawOver-Methode hilft uns dabei:

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

Die onDrawOver-Methode unterscheidet sich von der onDraw-Methode nur in der Renderreihenfolge. OnDrawOver-Dekorationen werden erst gerendert, nachdem das Listenelement selbst gezeichnet wurde.

Bei der onDrawOver-Methode müssen wir verstehen, an welchem ​​Punkt wir den Fortschritt zeichnen müssen, an welchem ​​Punkt wir ihn entfernen und die Listenelemente aktualisieren müssen, um den bereits eingerückten Einzug auszublenden.

Code zur Umsetzung der oben beschriebenen Maßnahmen:

  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 } 

Zeichenfortschritt


Der Rendering-Code ist ziemlich umfangreich und wird in einer separaten Datei dargestellt, auf die ich weiter unten eingehen werde.

Ich möchte nur auf die Nuancen eingehen, die für die Implementierung notwendig sind.

Alle Zeichenarbeiten werden auf Leinwand ausgeführt. Dementsprechend ist es erforderlich, eine Instanz des Paint-Objekts zu konfigurieren und Bögen zu zeichnen, die den Start- und Endwinkel angeben.

Eine wichtige Nuance ist, dass Sie, um so viel wie möglich ähnlich wie bei einer normalen ProgressBar zu rendern, ein eigenes Analogon eines Interpolators erstellen müssen, damit die Renderanimation nicht linear erfolgt.

Um die Arbeit von Interpolatoren zu verstehen und zu verstehen, was Sie von Ihrer Animation erhalten möchten, empfehle ich Ihnen, sich mit den Diagrammen vertraut zu machen, die die Arbeit verschiedener Arten von Interpolatoren darstellen, beispielsweise in diesem Artikel

Code-Referenzen:

PaginationLoadingDecoration
PaginationProgressDrawer

Bei Bedarf können Sie eine ProgressDrawer-Schnittstelle erstellen und Implementierungen in PaginationLoadingDecoration ersetzen.

Demo-Video herunterladen:


Vielen Dank fürs Lesen, viel Spaß beim Codieren :)

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


All Articles