Można to zrobić poprzez wdrożenie View#onSaveInstanceState
i View#onRestoreInstanceState
oraz rozszerzające View.BaseSavedState
klasę.
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];
}
};
}
}
Praca jest podzielona między klasę View i SavedState klasy View. Trzeba zrobić wszystko, pracę czytania i pisania do iz Parcel
w SavedState
klasie. Następnie klasa View może wykonać zadanie wyodrębnienia członków stanu i wykonania pracy niezbędnej do przywrócenia klasy do prawidłowego stanu.
Uwagi: View#onSavedInstanceState
i View#onRestoreInstanceState
są wywoływane automatycznie, jeśli View#getId
zwraca wartość> = 0. Dzieje się tak, gdy podajesz identyfikator w formacie xml lub wywołujesz setId
ręcznie. W przeciwnym razie trzeba zadzwonić View#onSaveInstanceState
i napisać Parcelable wrócił do działki można dostać się Activity#onSaveInstanceState
do zachowania stanu, a następnie ją przeczytać i przekazać go View#onRestoreInstanceState
od Activity#onRestoreInstanceState
.
Innym prostym przykładem tego jest CompoundButton
onSaveInstanceState()
ionRestoreInstanceState()
powinno byćprotected
(podobnie jak ich nadklasa), a niepublic
. Nie ma powodu, aby je ujawniać ...BaseSaveState
dla klasy, która rozszerza RecyclerView, dostajesz,Parcel﹕ Class not found when unmarshalling: android.support.v7.widget.RecyclerView$SavedState java.lang.ClassNotFoundException: android.support.v7.widget.RecyclerView$SavedState
więc musisz zrobić naprawioną usterkę, która jest zapisana tutaj: github.com/ksoichiro/Android-ObservableScrollView/commit/… (przy użyciu ClassLoader z RecyclerView.class, aby załadować super stan)Myślę, że jest to znacznie prostsza wersja.
Bundle
jest wbudowanym typem, który implementujeParcelable
źródło
onRestoreInstanceState
wezwany z pakietem, gdybyonSaveInstanceState
zwrócił pakiet?OnRestoreInstance
jest dziedziczony. Nie możemy zmienić nagłówka.Parcelable
jest tylko interfejsem,Bundle
jest implementacją tego.View
stanem bazowym nie jestBundle
. Oczywiście jest to w tej chwili prawda, ale polegasz na obecnym fakcie dotyczącym implementacji, który nie jest gwarantowany.Oto kolejny wariant, który wykorzystuje połączenie dwóch powyższych metod. Łącząc szybkość i poprawność
Parcelable
z prostotąBundle
:źródło
State
z paczki. Proszę spojrzeć na: charlesharley.com/2012/programming/…Odpowiedzi tutaj są już świetne, ale niekoniecznie działają w przypadku niestandardowych grup widoków. Aby wszystkie niestandardowe widoki zachowały swój stan, należy przesłonić
onSaveInstanceState()
ionRestoreInstanceState(Parcelable state)
w każdej klasie. Musisz także upewnić się, że wszystkie mają unikalne identyfikatory, niezależnie od tego, czy są zawyżone z XML lub dodane programowo.To, co wymyśliłem, było zadziwiająco podobne do odpowiedzi Kobor42, ale błąd pozostał, ponieważ programowo dodawałem Widoki do niestandardowej grupy ViewGroup i nie przypisywałem unikalnych identyfikatorów.
Łącze udostępnione przez mato będzie działać, ale oznacza to, że żaden z poszczególnych widoków nie zarządza własnym stanem - cały stan jest zapisywany w metodach ViewGroup.
Problem polega na tym, że gdy wiele z tych grup ViewGroup jest dodawanych do układu, identyfikatory ich elementów z pliku xml nie są już unikalne (jeśli są zdefiniowane w pliku xml). W czasie wykonywania można wywołać metodę statyczną,
View.generateViewId()
aby uzyskać unikalny identyfikator widoku. Jest to dostępne tylko w API 17.Oto mój kod z grupy ViewGroup (jest abstrakcyjny, a mOriginalValue jest zmienną typu):
źródło
Miałem problem, że onRestoreInstanceState przywrócił wszystkie moje niestandardowe widoki ze stanem ostatniego widoku. Rozwiązałem go, dodając te dwie metody do mojego widoku niestandardowego:
źródło
Zamiast używać
onSaveInstanceState
ionRestoreInstanceState
możesz także użyćViewModel
. Poszerz swój model danychViewModel
, abyś mógłViewModelProviders
uzyskać tę samą instancję swojego modelu za każdym razem, gdy działanie zostanie odtworzone:Do użytku
ViewModelProviders
, dodać następującedependencies
wapp/build.gradle
:Zauważ, że twoje
MyActivity
rozszerzeniaFragmentActivity
zamiast tylko przedłużaniaActivity
.Możesz przeczytać więcej o ViewModels tutaj:
źródło
ViewModel
jest szczególnie przydatny, jeśli masz duże zestawy danych do zachowania podczas zmiany stanu, takiej jak obrót ekranu. Wolę używaćViewModel
zamiast pisać w,Application
ponieważ ma wyraźny zakres i mogę mieć wiele działań tej samej aplikacji, które działają poprawnie.Przekonałem się, że ta odpowiedź spowodowała awarie w wersjach Androida 9 i 10. Myślę, że to dobre podejście, ale kiedy patrzyłem na kod Androida , okazało się, że brakuje konstruktora. Odpowiedź jest dość stara, więc prawdopodobnie nie było takiej potrzeby. Kiedy dodałem brakujący konstruktor i nazwałem go od twórcy, awaria została naprawiona.
Oto edytowany kod:
źródło
Aby rozszerzyć inne odpowiedzi - jeśli masz wiele niestandardowych widoków złożonych o tym samym identyfikatorze i wszystkie są przywracane ze stanem ostatniego widoku przy zmianie konfiguracji, wystarczy, że powiesz widokowi, aby wysyłał tylko zdarzenia zapisu / przywracania do siebie, zastępując kilka metod.
Aby dowiedzieć się, co się dzieje i dlaczego to działa, zobacz ten post na blogu . Zasadniczo identyfikatory widoków dzieci w widoku złożonym są wspólne dla każdego widoku złożonego, a przywracanie stanu jest mylone. Wyłącznie wysyłając stan dla samego widoku złożonego, zapobiegamy otrzymywaniu przez ich dzieci mieszanych wiadomości z innych widoków złożonych.
źródło