Ich habe eine Frage zu Android RecyclerView.State .
Ich verwende ein RecyclerView. Wie kann ich es verwenden und mit RecyclerView.State binden?
Mein Zweck ist es , die Bildlaufposition von RecyclerView zu speichern .
Ich habe eine Frage zu Android RecyclerView.State .
Ich verwende ein RecyclerView. Wie kann ich es verwenden und mit RecyclerView.State binden?
Mein Zweck ist es , die Bildlaufposition von RecyclerView zu speichern .
Wie wollen Sie die zuletzt gespeicherte Position mit speichern? RecyclerView.State
?
Sie können sich immer auf den guten Speicherzustand verlassen. Erweitern RecyclerView
und überschreiben Sie onSaveInstanceState () und onRestoreInstanceState()
:
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null && layoutManager instanceof LinearLayoutManager){
mScrollPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
}
SavedState newState = new SavedState(superState);
newState.mScrollPosition = mScrollPosition;
return newState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(state);
if(state != null && state instanceof SavedState){
mScrollPosition = ((SavedState) state).mScrollPosition;
LayoutManager layoutManager = getLayoutManager();
if(layoutManager != null){
int count = layoutManager.getItemCount();
if(mScrollPosition != RecyclerView.NO_POSITION && mScrollPosition < count){
layoutManager.scrollToPosition(mScrollPosition);
}
}
}
}
static class SavedState extends android.view.View.BaseSavedState {
public int mScrollPosition;
SavedState(Parcel in) {
super(in);
mScrollPosition = in.readInt();
}
SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mScrollPosition);
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
Aktualisieren
Ab der
recyclerview:1.2.0-alpha02
VeröffentlichungStateRestorationPolicy
wurde eingeführt. Es könnte eine bessere Herangehensweise an das gegebene Problem sein.Dieses Thema wurde in einem mittleren Artikel für Android-Entwickler behandelt .
Außerdem hat @ rubén-viguera in der folgenden Antwort weitere Details mitgeteilt. https://stackoverflow.com/a/61609823/892500
Alte Antwort
Wenn Sie LinearLayoutManager verwenden, wird die vorgefertigte API zum Speichern von linearLayoutManagerInstance.onSaveInstanceState () und zum Wiederherstellen der API linearLayoutManagerInstance.onRestoreInstanceState (...) mitgeliefert.
Damit können Sie das zurückgegebene Paket in Ihrem Außenstaat speichern. z.B,
und stellen Sie die Wiederherstellungsposition mit dem von Ihnen gespeicherten Status wieder her. z.B,
Zum Abschluss sieht Ihr endgültiger Code ungefähr so aus
Bearbeiten: Sie können dieselbe API auch mit dem GridLayoutManager verwenden, da es sich um eine Unterklasse von LinearLayoutManager handelt. Danke @wegsehen für den Vorschlag.
Bearbeiten: Denken Sie daran, dass Sie, wenn Sie auch Daten in einen Hintergrundthread laden, onRestoreInstanceState in Ihrer onPostExecute / onLoadFinished-Methode aufrufen müssen, damit die Position bei einer Änderung der Ausrichtung wiederhergestellt werden kann, z
quelle
RecyclerView
aber nicht, wenn Sie auf jeder Seite eineViewPager
mit einerRecycerView
haben.onCreateView
, sondern nach dem Laden der Daten. Siehe @ Farmaker Antwort hier.Geschäft
Wiederherstellen
und wenn das nicht funktioniert, versuchen Sie es
Speichern
onPause()
und wiederherstellenonResume()
quelle
lastFirstVisiblePosition
,0
nachdem Sie es wiederhergestellt haben. Dieser Fall ist nützlich, wenn Sie ein Fragment / eine Aktivität haben, die verschiedene Daten in eine lädtRecyclerView
, z. B. eine Datei-Explorer-App.SwipeRefreshLayout
?RecyclerView
Ich wollte die Bildlaufposition von Recycler View speichern, wenn ich von meiner Listenaktivität weg navigiere und dann auf die Schaltfläche "Zurück" klicke, um zurück zu navigieren. Viele der für dieses Problem bereitgestellten Lösungen waren entweder viel komplizierter als erforderlich oder funktionierten für meine Konfiguration nicht. Daher dachte ich, ich würde meine Lösung teilen.
Speichern Sie zuerst Ihren Instanzstatus in onPause, wie viele gezeigt haben. Ich denke, es ist hier hervorzuheben, dass diese Version von onSaveInstanceState eine Methode aus der RecyclerView.LayoutManager-Klasse ist.
Der Schlüssel, damit dies ordnungsgemäß funktioniert, besteht darin, sicherzustellen, dass Sie onRestoreInstanceState aufrufen, nachdem Sie Ihren Adapter angeschlossen haben, wie einige in anderen Threads angegeben haben. Der eigentliche Methodenaufruf ist jedoch viel einfacher als von vielen angegeben.
quelle
I Setzen Sie Variablen in onCreate (), speichern Sie die Bildlaufposition in onPause () und setzen Sie die Bildlaufposition in onResume ()
quelle
Sie müssen den Status nicht mehr selbst speichern und wiederherstellen. Wenn Sie eine eindeutige ID in XML festlegen und recyclerView.setSaveEnabled (true) (standardmäßig true), wird dies vom System automatisch ausgeführt. Hier ist mehr dazu: http://trickyandroid.com/saving-android-view-state-correctly/
quelle
Alle in der Support-Bibliothek enthaltenen Layout-Manager wissen bereits, wie die Bildlaufposition gespeichert und wiederhergestellt wird.
Die Bildlaufposition wird beim ersten Layoutdurchlauf wiederhergestellt, wenn die folgenden Bedingungen erfüllt sind:
RecyclerView
RecyclerView
In der Regel legen Sie den Layout-Manager in XML fest, sodass Sie jetzt nur noch etwas tun müssen
RecyclerView
Sie können dies jederzeit tun, es ist nicht auf
Fragment.onCreateView
oder beschränktActivity.onCreate
.Beispiel: Stellen Sie sicher, dass der Adapter bei jeder Aktualisierung der Daten angeschlossen ist.
Die gespeicherte Bildlaufposition wird aus folgenden Daten berechnet:
Daher können Sie leichte Abweichungen erwarten, z. B. wenn Ihre Artikel im Hoch- und Querformat unterschiedliche Abmessungen haben.
Das
RecyclerView
/ScrollView
/ was auch immer muss ein haben,android:id
damit sein Zustand gespeichert wird.quelle
LinearLayoutManager.SavedState
: cs.android.com/androidx/platform/frameworks/support/+/…Hier ist mein Ansatz, um sie zu speichern und den Status der Recyclingübersicht in einem Fragment beizubehalten. Fügen Sie zunächst Konfigurationsänderungen in Ihrer übergeordneten Aktivität wie im folgenden Code hinzu.
Deklarieren Sie dann in Ihrem Fragment diese Instanzmethoden
Fügen Sie den folgenden Code in Ihre @ onPause-Methode ein
Fügen Sie die onConfigartionChanged-Methode wie unten hinzu.
Dieser Ansatz wird Ihr Problem beheben. Wenn Sie einen anderen Layout-Manager haben, ersetzen Sie einfach den oberen Layout-Manager.
quelle
Auf diese Weise stelle ich die RecyclerView-Position mit GridLayoutManager nach der Drehung wieder her, wenn Sie mit AsyncTaskLoader Daten aus dem Internet neu laden müssen.
Erstellen Sie eine globale Variable aus Parcelable und GridLayoutManager sowie eine statische endgültige Zeichenfolge:
Speichern Sie den Status von gridLayoutManager in onSaveInstance ()
In onRestoreInstanceState wiederherstellen
Wenn der Loader Daten aus dem Internet abruft, stellen Sie die Position der Recycling-Ansicht in onLoadFinished () wieder her.
..natürlich müssen Sie gridLayoutManager in onCreate instanziieren. Prost
quelle
Ich hatte die gleichen Anforderungen, aber die Lösungen hier haben mich aufgrund der Datenquelle für die recyclerView nicht ganz über die Linie gebracht.
Ich habe den LinearLayoutManager-Status von RecyclerViews in onPause () extrahiert.
Das Paket
state
wird gespeichertonSaveInstanceState(Bundle outState)
und erneut extrahiertonCreate(Bundle savedInstanceState)
, wennsavedInstanceState != null
.Der recyclerView-Adapter wird jedoch von einem ViewModel LiveData-Objekt gefüllt und aktualisiert, das von einem DAO-Aufruf an eine Room-Datenbank zurückgegeben wird.
Ich fand schließlich heraus, dass das Wiederherstellen des Instanzstatus direkt nach dem Festlegen der Daten im Adapter den Bildlaufstatus über Rotationen hinweg beibehalten würde.
Ich vermute, das liegt auch daran, dass die
LinearLayoutManager
dass der wiederhergestellte Status überschrieben wurde, als die Daten vom Datenbankaufruf zurückgegeben wurden, oder dass der wiederhergestellte Status für einen 'leeren' LinearLayoutManager bedeutungslos war.Wenn die Adapterdaten direkt verfügbar sind (dh bei einem Datenbankaufruf nicht relevant sind), kann der Instanzstatus im LinearLayoutManager wiederhergestellt werden, nachdem der Adapter in der recyclerView festgelegt wurde.
Die Unterscheidung zwischen den beiden Szenarien hat mich ewig aufgehalten.
quelle
Auf der Android-API-Ebene 28 stelle ich einfach sicher, dass ich meine
LinearLayoutManager
undRecyclerView.Adapter
meineFragment#onCreateView
Methode sowie alles, was gerade funktioniert hat, eingerichtet habe. Ich brauche nicht zu tunonSaveInstanceState
oder zuonRestoreInstanceState
arbeiten.Die Antwort von Eugen Pechanec erklärt, warum dies funktioniert.
quelle
Ab Version 1.2.0-alpha02 der androidx recyclerView-Bibliothek wird diese nun automatisch verwaltet. Fügen Sie es einfach hinzu mit:
Und benutze:
Die Aufzählung StateRestorationPolicy bietet drei Optionen:
Beachten Sie, dass sich die recyclerView-Bibliothek zum Zeitpunkt dieser Antwort noch in alpha03 befindet, die alpha-Phase jedoch nicht für Produktionszwecke geeignet ist .
quelle
Für mich bestand das Problem darin, dass ich jedes Mal, wenn ich meinen Adapter wechselte, einen neuen Layoutmanager einrichtete und die Position des Bildlaufs in recyclerView verlor.
quelle
Ich möchte nur die jüngste Situation mit RecyclerView teilen. Ich hoffe, dass jeder, der das gleiche Problem hat, davon profitieren wird.
Meine Projektanforderung: Ich habe also eine RecyclerView, in der einige anklickbare Elemente in meiner Hauptaktivität (Aktivität A) aufgelistet sind. Wenn Sie auf den Artikel klicken, wird eine neue Aktivität mit den Artikeldetails (Aktivität-B) angezeigt.
Ich habe im Manifest implementiert Datei dass Aktivität-A das übergeordnete Element von Aktivität-B ist. Auf diese Weise habe ich eine Zurück- oder Startschaltfläche in der Aktionsleiste von Aktivität-B
Problem: Jedes Mal, wenn ich die Zurück- oder Home-Taste in der Aktionsleiste von Aktivität B gedrückt habe, wechselt Aktivität A ab onCreate () in den vollständigen Aktivitätslebenszyklus.
Obwohl ich ein onSaveInstanceState () implementiert habe, das die Liste des RecyclerView-Adapters speichert, ist saveInstanceState in der onCreate () -Methode immer null, wenn Activity-A seinen Lebenszyklus startet.
Beim weiteren Durchsuchen des Internets stieß ich auf das gleiche Problem, aber die Person bemerkte, dass die Schaltfläche Zurück oder Startseite unter dem Anroid-Gerät (Standard-Schaltfläche Zurück / Startseite), Aktivität-A, nicht in den Aktivitätslebenszyklus eingeht.
Lösung:
Ich habe die Home- oder Back-Taste in Aktivität B aktiviert
Fügen Sie unter der Methode onCreate () diese Zeile hinzu. SupportActionBar? .SetDisplayHomeAsUpEnabled (true)
Bei der Methode overide fun onOptionsItemSelected () habe ich nach dem Element gesucht. Element-ID, auf die das Element basierend auf der ID geklickt wird. Die ID für die Zurück-Schaltfläche lautet
android.R.id.home
Implementieren Sie dann einen Funktionsaufruf finish () im android.R.id.home
Dies beendet die Aktivität B und bringt Aktivität A, ohne den gesamten Lebenszyklus zu durchlaufen.
Für meine Anforderung ist dies die bisher beste Lösung.
quelle
Ich hatte das gleiche Problem mit einem kleinen Unterschied, ich wurde mit Databinding , und ich war recyclerView Adapter und Layoutmanager in den Bindeverfahren setzen.
Das Problem war , dass die Daten-Bindung wurde nach geschieht onResume so war es mein Layout nach onResume Zurücksetzen und das bedeutet , es wurde jedes Mal zurück zum ursprünglichen Platz zu scrollen. Wenn ich die Einstellung von layoutManager in den Datenbindungsadaptermethoden beendet habe, funktioniert dies wieder einwandfrei.
quelle
In meinem Fall habe ich die RecyclerViews
layoutManager
sowohl in XML als auch in festgelegtonViewCreated
. Das Entfernen der Zuordnung wurdeonViewCreated
behoben.quelle
Activity.java:
Warum ich
OnSaveInstanceState()
inonPause()
Mitteln verwende, während der Wechsel zu einer anderen Aktivität onPause aufgerufen wird. Diese Bildlaufposition wird gespeichert und die Position wiederhergestellt, wenn wir von einer anderen Aktivität zurückkehren.quelle