webentwicklung-frage-antwort-db.com.de

Daten in RecyclerView aktualisieren und die Bildlaufposition beibehalten

Wie kann man die Daten aktualisieren, die in RecyclerView angezeigt werden (Aufruf von notifyDataSetChanged auf seinem Adapter), und sicherstellen, dass die Bildlaufposition genau auf den ursprünglichen Wert zurückgesetzt wird? 

Im Falle eines guten alten 'ListView' genügt es, getChildAt(0) abzurufen, getTop() zu überprüfen und setSelectionFromTop mit den gleichen exakten Daten aufzurufen.

Bei RecyclerView scheint es nicht möglich zu sein.

Ich nehme an, ich sollte seine LayoutManager verwenden, die zwar scrollToPositionWithOffset(int position, int offset) liefert, aber wie kann ich die Position und den Versatz richtig abrufen? 

layoutManager.findFirstVisibleItemPosition() und layoutManager.getChildAt(0).getTop()

Oder gibt es einen eleganteren Weg, um die Arbeit zu erledigen?

61
Konrad Morawski

Ich benutze diese. ^ _ ^

// Save state
private Parcelable recyclerViewState;
recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();

// Restore state
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);

Es ist einfacher, ich hoffe es hilft dir!

92
DawnYu

Ich habe ein ganz ähnliches Problem. Und ich habe folgende Lösung gefunden.

Die Verwendung von notifyDataSetChanged ist keine gute Idee. Sie sollten genauer sein, dann speichert RecyclerView den Bildlaufstatus für Sie.

Wenn Sie beispielsweise nur eine Aktualisierung durchführen müssen, oder anders ausgedrückt, Sie möchten, dass jede Ansicht erneut gebannt wird, führen Sie einfach Folgendes aus:

adapter.notifyItemRangeChanged(0, adapter.getItemCount());
43
Kaerdan

EDIT: Um die gleiche scheinbare Position wiederherzustellen, wie in, lassen Sie es genau so aussehen, wie es aussah. Wir müssen etwas anders machen (siehe unten, um den genauen Wert für scrollY wiederherzustellen):

Speichern Sie die Position und versetzen Sie sie wie folgt:

LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager();
int firstItem = manager.findFirstVisibleItemPosition();
View firstItemView = manager.findViewByPosition(firstItem);
float topOffset = firstItemView.getTop();

outState.putInt(ARGS_SCROLL_POS, firstItem);
outState.putFloat(ARGS_SCROLL_OFFSET, topOffset);

Und dann die Schriftrolle wie folgt wiederherstellen:

LinearLayoutManager manager = (LinearLayoutManager) mRecycler.getLayoutManager();
manager.scrollToPositionWithOffset(mStatePos, (int) mStateOffset);

Dadurch wird die exakte Position scheinbar der Liste wiederhergestellt. Anscheinend, weil es für den Benutzer gleich aussieht, aber nicht denselben scrollY-Wert hat (wegen möglicher Unterschiede in den Abmessungen des Quer-/Hochformat-Layouts).

Beachten Sie, dass dies nur mit LinearLayoutManager funktioniert.

--- Nachfolgend wird beschrieben, wie Sie das genaue scrollY wiederherstellen können, wodurch die Liste wahrscheinlich anders aussieht ---

  1. Wenden Sie einen OnScrollListener wie folgt an:

    private int mScrollY;
    private RecyclerView.OnScrollListener mTotalScrollListener = new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            mScrollY += dy;
        }
    };
    

Dadurch wird die genaue Bildlaufposition jederzeit in mScrollY gespeichert. 

  1. Speichern Sie diese Variable in Ihrem Bundle und stellen Sie sie nach einer anderen Variablen wieder her. Wir nennen sie mStateScrollY.

  2. Nachdem der Zustand wiederhergestellt wurde und after, hat Ihr RecyclerView alle seine Daten zurückgesetzt.

    mRecyclerView.scrollBy(0, mStateScrollY);
    

Das ist es. 

Beachten Sie, dass Sie den Bildlauf auf eine andere Variable zurücksetzen. Dies ist wichtig, da der OnScrollListener mit .scrollBy () aufgerufen wird und anschließend mScrollY auf den in mStateScrollY gespeicherten Wert setzt. Wenn Sie dies nicht tun, hat mScrollY den doppelten Scrollwert (da der OnScrollListener mit Deltas und nicht mit absoluten Scrolls arbeitet).

Staatliche Einsparungen bei Aktivitäten können wie folgt erzielt werden:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(ARGS_SCROLL_Y, mScrollY);
}

Und zum Wiederherstellen rufen Sie dies in Ihrem onCreate () auf:

if(savedState != null){
    mStateScrollY = savedState.getInt(ARGS_SCROLL_Y, 0);
}

Das Speichern von Fragmenten in Fragmenten funktioniert auf ähnliche Weise, aber das eigentliche Speichern von Status erfordert etwas zusätzliche Arbeit, aber es gibt viele Artikel, die sich damit befassen. Sie sollten also kein Problem damit haben, herauszufinden, wie die Prinzipien des Speicherns von scrollY aussehen und das Wiederherstellen bleibt gleich.

20
A. Steenbergen

Ja, Sie können dieses Problem beheben, indem Sie den Adapterkonstruktor nur einmal erstellen. Ich erkläre hier den Codierungsteil:

if (appointmentListAdapter == null) {
        appointmentListAdapter = new AppointmentListAdapter(AppointmentsActivity.this);
        appointmentListAdapter.addAppointmentListData(appointmentList);
        appointmentListAdapter.setOnStatusChangeListener(onStatusChangeListener);
        appointmentRecyclerView.setAdapter(appointmentListAdapter);

    } else {
        appointmentListAdapter.addAppointmentListData(appointmentList);
        appointmentListAdapter.notifyDataSetChanged();
    }

Jetzt können Sie sehen, dass ich überprüft habe, ob der Adapter null ist oder nicht und erst dann initialisieren, wenn er null ist.

Wenn der Adapter nicht null ist, kann ich versichern, dass ich meinen Adapter mindestens einmal initialisiert habe. 

Also füge ich dem Adapter einfach eine Liste hinzu und rufe notifydatasetchanged auf.

RecyclerView hält immer die letzte gescrollte Position, daher müssen Sie die letzte Position nicht speichern. Rufen Sie notifydydat aetchangedanged auf.

Danke Viel Spaß beim Codieren 

5

Die Top-Antwort von @DawnYu funktioniert, aber die Recycling-Übersicht scrollt zuerst nach oben und kehrt dann zur gewünschten Scroll-Position zurück, was zu einer "flimmerartigen" Reaktion führt, die nicht angenehm ist.

Um die recyclerView zu aktualisieren, insbesondere nachdem Sie von einer anderen Aktivität gekommen sind, ohne zu flackern und die Bildlaufposition beizubehalten, müssen Sie die folgenden Schritte ausführen.

  1. Stellen Sie sicher, dass Sie Ihre Recycler-Ansicht mit DiffUtil aktualisieren. Lesen Sie mehr darüber hier: https://www.journaldev.com/20873/Android-recyclerview-diffutil
  2. Laden Sie die Daten zu dem Zeitpunkt, zu dem Sie Ihre Aktivität aktualisieren möchten, in Ihre Recyclingübersicht. Mit diffUtil werden nur die Aktualisierungen in der Recyclingübersicht vorgenommen, während die Position beibehalten wird.

Hoffe das hilft.

1
Moses Mwongela
 mMessageAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
        @Override
        public void onChanged() {
            mLayoutManager.smoothScrollToPosition(mMessageRecycler, null, mMessageAdapter.getItemCount());
        }
    });

Die Lösung besteht darin, beim Eintreffen einer neuen Nachricht weiter durch die Recyclingübersicht zu scrollen.

Die onChanged () -Methode erkennt die in recyclerview ausgeführte Aktion.

0

Ich habe Recyclerview nicht verwendet, aber ich habe es in ListView gemacht. Beispielcode in Recyclerview:

setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        rowPos = mLayoutManager.findFirstVisibleItemPosition();

Es ist der Listener, wenn der Benutzer scrollt. Der Leistungsaufwand ist nicht signifikant. Und die erste sichtbare Position ist auf diese Weise genau.

Behalten Sie die Bildlaufposition bei, indem Sie @DawnYu answer verwenden, um notifyDataSetChanged() wie folgt umzubrechen:

val recyclerViewState = recyclerView.layoutManager?.onSaveInstanceState() 
adapter.notifyDataSetChanged() 
recyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState)
0

Das funktioniert bei mir in Kotlin.

  1. Erstellen Sie den Adapter und übergeben Sie Ihre Daten im Konstruktor
class LEDRecyclerAdapter (var currentPole: Pole): RecyclerView.Adapter<RecyclerView.ViewHolder>()  { ... }
  1. ändere diese Eigenschaft und rufe notifyDataSetChanged () auf
adapter.currentPole = pole
adapter.notifyDataSetChanged()

Der Scroll-Offset ändert sich nicht.

0
Chris8447