Sie tun dies durch die Implementierung View#onSaveInstanceState
und View#onRestoreInstanceState
und die Verlängerung View.BaseSavedState
Klasse.
public class CustomView extends View {
private int stateToSave;
...
@Override
public Parcelable onSaveInstanceState() {
//begin boilerplate code that allows parent classes to save state
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
//end
ss.stateToSave = this.stateToSave;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
//begin boilerplate code so parent classes can restore state
if(!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState)state;
super.onRestoreInstanceState(ss.getSuperState());
//end
this.stateToSave = ss.stateToSave;
}
static class SavedState extends BaseSavedState {
int stateToSave;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
this.stateToSave = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(this.stateToSave);
}
//required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
Die Arbeit wird zwischen der View- und der SavedState-Klasse der View aufgeteilt. Sie sollten die ganze Arbeit des Lesens und Schreibens zu und von Parcel
der SavedState
Klasse erledigen . Anschließend kann Ihre View-Klasse die Statusmitglieder extrahieren und die erforderlichen Arbeiten ausführen, um die Klasse wieder in einen gültigen Status zu versetzen.
Hinweise: View#onSavedInstanceState
und View#onRestoreInstanceState
werden automatisch für Sie aufgerufen, wenn View#getId
ein Wert> = 0 zurückgegeben wird. Dies geschieht, wenn Sie ihm eine ID in XML geben oder setId
manuell aufrufen . Andernfalls müssen Sie anrufen View#onSaveInstanceState
und schreiben die Parcel auf das Paket zurück Sie erhalten Activity#onSaveInstanceState
den Zustand zu speichern und sie anschließend und es passieren lesen View#onRestoreInstanceState
aus Activity#onRestoreInstanceState
.
Ein weiteres einfaches Beispiel hierfür ist das CompoundButton
onSaveInstanceState()
undonRestoreInstanceState()
sollteprotected
(wie ihre Oberklasse) nicht seinpublic
. Kein Grund, sie zu entlarven ...BaseSaveState
Klasse für eine Klasse speichern , die RecyclerView erweitert.Parcel﹕ Class not found when unmarshalling: android.support.v7.widget.RecyclerView$SavedState java.lang.ClassNotFoundException: android.support.v7.widget.RecyclerView$SavedState
Sie müssen daher die hier beschriebene Fehlerbehebung durchführen: github.com/ksoichiro/Android-ObservableScrollView/commit/… (mithilfe des ClassLoader von RecyclerView.class zum Laden des Super-Status)Ich denke, das ist eine viel einfachere Version.
Bundle
ist ein eingebauter Typ, der implementiertParcelable
quelle
onRestoreInstanceState
mit einem Bundle angerufen werden, wenn manonSaveInstanceState
ein Bundle zurückgibt?OnRestoreInstance
wird geerbt. Wir können den Header nicht ändern.Parcelable
ist nur eine Schnittstelle,Bundle
ist eine Implementierung dafür.View
kein a istBundle
. Natürlich ist das im Moment wahr, aber Sie verlassen sich auf diese aktuelle Implementierung, die nicht garantiert wahr ist.Hier ist eine andere Variante, die eine Mischung der beiden oben genannten Methoden verwendet. Kombinieren Sie die Geschwindigkeit und Korrektheit von
Parcelable
mit der Einfachheit einesBundle
:quelle
State
vom Paket instanziiert . Bitte werfen Sie einen Blick auf: charlesharley.com/2012/programming/…Die Antworten hier sind bereits großartig, funktionieren aber nicht unbedingt für benutzerdefinierte ViewGroups. Damit alle benutzerdefinierten Ansichten ihren Status beibehalten, müssen Sie
onSaveInstanceState()
undonRestoreInstanceState(Parcelable state)
in jeder Klasse überschreiben . Sie müssen auch sicherstellen, dass alle eindeutige IDs haben, unabhängig davon, ob sie aus XML aufgeblasen oder programmgesteuert hinzugefügt wurden.Was ich mir ausgedacht habe, war bemerkenswert ähnlich wie die Antwort von Kobor42, aber der Fehler blieb bestehen, weil ich die Ansichten programmgesteuert zu einer benutzerdefinierten ViewGroup hinzufügte und keine eindeutigen IDs zuwies.
Der von mato freigegebene Link funktioniert, bedeutet jedoch, dass keine der einzelnen Ansichten ihren eigenen Status verwaltet. Der gesamte Status wird in den ViewGroup-Methoden gespeichert.
Das Problem ist, dass beim Hinzufügen mehrerer dieser ViewGroups zu einem Layout die IDs ihrer Elemente aus der XML nicht mehr eindeutig sind (sofern sie in XML definiert sind). Zur Laufzeit können Sie die statische Methode aufrufen
View.generateViewId()
, um eine eindeutige ID für eine Ansicht abzurufen. Dies ist nur über API 17 verfügbar.Hier ist mein Code aus der ViewGroup (er ist abstrakt und mOriginalValue ist eine Typvariable):
quelle
Ich hatte das Problem, dass onRestoreInstanceState alle meine benutzerdefinierten Ansichten mit dem Status der letzten Ansicht wiederherstellte. Ich habe es gelöst, indem ich meiner benutzerdefinierten Ansicht diese beiden Methoden hinzugefügt habe:
quelle
Anstelle von
onSaveInstanceState
undonRestoreInstanceState
können Sie auch a verwendenViewModel
. Erweitern Sie Ihr DatenmodellViewModel
, und verwenden Sie es dannViewModelProviders
, um bei jeder Neuerstellung der Aktivität dieselbe Instanz Ihres Modells abzurufen:Benutzen
ViewModelProviders
Folgendes zudependencies
in hinzuapp/build.gradle
:Beachten Sie, dass Sie sich
MyActivity
erweitern,FragmentActivity
anstatt nur zu erweiternActivity
.Weitere Informationen zu ViewModels finden Sie hier:
quelle
ViewModel
Dies ist besonders praktisch, wenn Sie während einer Statusänderung große Datenmengen behalten müssen, z. B. bei einer Bildschirmdrehung. Ich bevorzuge es, das zu verwenden,ViewModel
anstatt es zu schreiben,Application
da es einen klaren Umfang hat und ich mehrere Aktivitäten derselben Anwendung haben kann, die sich korrekt verhalten.Ich fand heraus, dass diese Antwort einige Abstürze in den Android-Versionen 9 und 10 verursachte. Ich denke, es ist ein guter Ansatz, aber als ich mir einen Android-Code ansah ich fest, dass ein Konstruktor fehlte. Die Antwort ist ziemlich alt, so dass es zu der Zeit wahrscheinlich keine Notwendigkeit dafür gab. Als ich den fehlenden Konstruktor hinzufügte und ihn vom Ersteller aus aufrief, wurde der Absturz behoben.
Also hier ist der bearbeitete Code:
quelle
Um andere Antworten zu erweitern: Wenn Sie mehrere benutzerdefinierte zusammengesetzte Ansichten mit derselben ID haben und alle mit dem Status der letzten Ansicht bei einer Konfigurationsänderung wiederhergestellt werden, müssen Sie der Ansicht lediglich mitteilen, dass nur Speicher- / Wiederherstellungsereignisse ausgelöst werden sollen zu sich selbst durch Überschreiben einiger Methoden.
Eine Erklärung, was passiert und warum dies funktioniert, finden Sie in diesem Blogbeitrag . Grundsätzlich werden die IDs der untergeordneten Ansicht Ihrer zusammengesetzten Ansicht von jeder zusammengesetzten Ansicht gemeinsam genutzt, und die Wiederherstellung des Status wird verwirrt. Indem wir nur den Status für die zusammengesetzte Ansicht selbst senden, verhindern wir, dass ihre Kinder gemischte Nachrichten aus anderen zusammengesetzten Ansichten erhalten.
quelle