Wie werden RecyclerView-Elemente so ausgerichtet, dass alle X-Elemente als eine Einheit betrachtet werden, an der sie einrasten?

8

Hintergrund

Es ist möglich, einen RecyclerView mit Hilfe der folgenden Tasten zu zentrieren:

%Vor%

Beispiel:

MainActivity.kt

%Vor%

activity_main.xml

%Vor%

Es ist auch möglich, es auf andere Seiten einzufügen, wie dies in einigen Bibliotheken der Fall war, zB hier .

Es gibt auch Bibliotheken, die eine RecyclerView erlauben, die wie ein ViewPager funktionieren kann, zB hier .

Das Problem

Angenommen, ich habe eine RecyclerView (horizontal in meinem Fall) mit vielen Elementen, und ich möchte, dass es alle X Elemente (X ist konstant) als eine Einheit behandelt und an jeder dieser Einheiten einrastet.

Wenn ich zum Beispiel ein wenig blättere, könnte es entweder an der 0-Position oder an der X-Position einrasten, aber nicht an etwas dazwischen.

In gewisser Weise ähnelt es in seinem Verhalten einem normalen ViewPager, nur dass jede Seite X-Elemente enthält.

Wenn wir beispielsweise von dem Beispielcode ausgehen, den ich oben geschrieben habe, angenommen, X == 3, würde das Fangen aus diesem Leerlaufzustand stammen:

in diesen Ruhezustand (falls wir genug gescrollt haben, sonst im vorherigen Zustand bleiben):

Das Weiterleiten oder Scrollen von mehr sollte wie auf ViewPager behandelt werden, genau wie die Bibliothek, die ich oben erwähnt habe.

Wenn Sie mehr (in die gleiche Richtung) zum nächsten Fangpunkt scrollen, erreichen Sie den Punkt "6", "9" und so weiter ...

Was ich versucht habe

Ich habe versucht, nach alternativen Bibliotheken zu suchen, und ich habe auch versucht, die Dokumente diesbezüglich zu lesen, aber ich habe nichts gefunden, was nützlich sein könnte.

Es könnte auch möglich sein, einen ViewPager zu verwenden, aber ich denke, das ist nicht der beste Weg, da ViewPager seine Elemente nicht gut wiederverwenden kann und ich glaube, dass es weniger flexibel ist als RecyclerView in Bezug auf Snap.

Die Fragen

  1. Ist es möglich, RecyclerView so einzustellen, dass alle X-Elemente gefangen werden, um jedes X-Objekt als eine einzelne Seite zu behandeln, an der es einrasten soll?

    Natürlich, die Gegenstände nehmen genug Platz für den gesamten RecyclerView, gleichmäßig.

  2. Angenommen, es ist möglich, wie würde ich einen Rückruf erhalten, wenn der RecyclerView gerade an einem bestimmten Gegenstand einrastet, einschließlich dieses Gegenstandes, bevor er geschnappt wurde? Ich frage das, weil es sich auf dieselbe Frage bezieht, die ich hier gestellt habe.

Kotlin Lösung

Eine funktionierende Kotlin-Lösung, die auf "Cheticamp" basiert ( hier ), ohne dass dies nötig ist Vergewissern Sie sich, dass Sie die RecyclerView-Größe und die Auswahl eines Gitters anstelle einer Liste im Beispiel haben:

MainActivity.kt

%Vor%

SnapToBlock.kt

%Vor%

Aktualisieren

Auch wenn ich eine Antwort als akzeptiert markiert habe, wie es funktioniert, Ich habe bemerkt, dass es ernsthafte Probleme gibt:

  1. Smooth Scrolling scheint nicht gut zu funktionieren (scrollt nicht zum richtigen Ort). Scrollen nur diese Arbeit ist so (aber mit dem "Verwischen" -Effekt):

    %Vor%
  2. Wenn ich auf RTL (right to left) wie Hebräisch ("עבעבית") umschalte, kann ich nicht blättern.

  3. Ich habe bemerkt, dass onCreateViewHolder viel heißt. Tatsächlich wird es jedes Mal aufgerufen, wenn ich scroll, selbst für Zeiten, in denen es die ViewHolder hätte recyceln sollen. Dies bedeutet, dass eine übermäßige Erstellung von Ansichten und möglicherweise auch ein Speicherleck vorliegt.

Ich habe versucht, diese selbst zu beheben, aber bisher gescheitert.

Wenn jemand hier weiß, wie man es beheben kann, werde ich das zusätzliche, neue Kopfgeld gewähren

Update: Da wir eine Korrektur für RTL / LTR haben, habe ich die Kotlin-Lösung in diesem Beitrag aktualisiert.

    
android developer 27.11.2017, 15:07
quelle

2 Antworten

15

SnapHelper liefert den notwendigen Rahmen für das, was Sie versuchen, aber es muss erweitert werden, um Blöcke von Sichten zu behandeln. Die Klasse SnapToBlock unten erweitert SnapHelper auf Blöcke von Ansichten. Im Beispiel habe ich vier Ansichten zu einem Block verwendet, aber es kann mehr oder weniger sein.

Update: Der Code wurde geändert, um sowohl GridLayoutManager als auch LinearLayoutManager zu berücksichtigen. Das Schleudern ist jetzt gesperrt, so dass die Fangfunktion mehr eine ViewPager auflistet. Horizontales und vertikales Scrollen wird jetzt ebenso unterstützt wie LTR- und RTL-Layouts.

Update: Glatter Bildlaufinterpolator wurde so geändert, dass er mehr wie ViewPager aussieht.

Update: Hinzufügen von Callbacks für Pre / Post-Snapping.

Update: Hinzufügen von Unterstützung für RTL-Layouts.

Hier ist ein kurzes Video der Beispiel-App:

Richten Sie den Layout-Manager wie folgt ein:

%Vor%

Fügen Sie Folgendes hinzu, um SnapToBlock an RecyclerView anzuhängen.

%Vor%

mMaxFlingPages ist die maximale Anzahl von Blöcken (rowsCols * span), die es erlauben, gleichzeitig ausgelöst zu werden.

Für Rückrufe, wenn ein Snap gemacht werden soll und abgeschlossen wurde, fügen Sie Folgendes hinzu:

%Vor%

SnapToBlock.java

%Vor%

Die oben definierte Schnittstelle SnapBlockCallback kann verwendet werden, um die Adapterposition der Ansicht am Anfang des zu schnappenden Blocks zu melden. Die Ansicht, die dieser Position zugeordnet ist, wird möglicherweise nicht instanziiert, wenn der Anruf ausgeführt wird, wenn die Ansicht nicht auf dem Bildschirm angezeigt wird.

    
Cheticamp 30.11.2017, 18:57
quelle
1

Ich würde so etwas tun

  1. Blockieren Sie den Bildlauf innerhalb von RecyclerView (z. B. So können Sie das RecyclerView-Scrollen deaktivieren? )

  2. Erstellen Sie Gesture Fling Detector und hängen Sie es an RecyclerView an

  3. Inside Gesture Detector erkennt Ereignisse von Schleuderereignissen
  4. Beim Fling-Ereignis: Erkenne die Seite (links rechts)
  5. Scroll RecyclerView zur Positionierung (erstes Sichtbares + const * (links? -1: 1))

sollte funktionieren:)

    
wojciech_maciejewski 27.11.2017 15:54
quelle