Вы можете сделать это путем внедрения View#onSaveInstanceState
и View#onRestoreInstanceState
и расширения View.BaseSavedState
класса.
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];
}
};
}
}
Работа разделена между представлением и классом представления SavedState. Вы должны сделать все работу чтения и записи и от Parcel
в SavedState
классе. Затем ваш класс View может выполнить работу по извлечению членов состояния и выполнению работы, необходимой для возвращения класса в действительное состояние.
Примечания: View#onSavedInstanceState
и View#onRestoreInstanceState
вызываются автоматически для вас, еслиView#getId
возвращает значение> = 0. Это происходит, когда вы даете ему идентификатор в xml или вызываете setId
вручную. В противном случае вы должны позвонить View#onSaveInstanceState
и написать Parcelable вернулся к посылке вы получаете в , Activity#onSaveInstanceState
чтобы сохранить состояние , а затем прочитать его и передать его View#onRestoreInstanceState
с Activity#onRestoreInstanceState
.
Другим простым примером этого является CompoundButton
onSaveInstanceState()
иonRestoreInstanceState()
должно бытьprotected
(как их суперкласс), а неpublic
. Нет причин выставлять их ...BaseSaveState
для класса, который расширяет RecyclerView, вы получаете,Parcel﹕ Class not found when unmarshalling: android.support.v7.widget.RecyclerView$SavedState java.lang.ClassNotFoundException: android.support.v7.widget.RecyclerView$SavedState
поэтому вам нужно сделать исправление ошибки, которое записано здесь: github.com/ksoichiro/Android-ObservableScrollView/commit/… (используя ClassLoader из RecyclerView.class для загрузки супер состояния)Я думаю, что это гораздо более простая версия.
Bundle
это встроенный тип, который реализуетParcelable
источник
onRestoreInstanceState
вызвать с Bundle, еслиonSaveInstanceState
вернули Bundle?OnRestoreInstance
наследуется Мы не можем изменить заголовок.Parcelable
это просто интерфейс,Bundle
это реализация для этого.View
состояние не являетсяBundle
. Конечно, сейчас это так, но вы полагаетесь на этот факт реализации, который не гарантирован.Вот еще один вариант, который использует сочетание двух вышеуказанных методов. Сочетая скорость и правильность
Parcelable
с простотойBundle
:источник
State
из посылки. Пожалуйста , обратите внимание на: charlesharley.com/2012/programming/...Ответы здесь уже хороши, но не обязательно работают для пользовательских ViewGroups. Чтобы все пользовательские представления сохранили свое состояние, необходимо переопределить
onSaveInstanceState()
иonRestoreInstanceState(Parcelable state)
в каждом классе. Вы также должны убедиться, что все они имеют уникальные идентификаторы, независимо от того, накачаны они из xml или добавлены программно.То, что я придумал, было удивительно похоже на ответ Kobor42, но ошибка осталась, потому что я программно добавлял виды в пользовательскую группу ViewGroup, а не назначал уникальные идентификаторы.
Ссылка, совместно используемая mato, будет работать, но это означает, что ни один из отдельных видов не управляет своим собственным состоянием - все состояние сохраняется в методах ViewGroup.
Проблема заключается в том, что когда несколько из этих ViewGroups добавляются в макет, идентификаторы их элементов из XML больше не являются уникальными (если они определены в XML). Во время выполнения вы можете вызвать статический метод,
View.generateViewId()
чтобы получить уникальный идентификатор для представления. Это доступно только из API 17.Вот мой код из ViewGroup (он абстрактный, а mOriginalValue является переменной типа):
источник
У меня была проблема, что onRestoreInstanceState восстановил все мои пользовательские представления с состоянием последнего представления. Я решил это, добавив эти два метода в мой пользовательский вид:
источник
Вместо использования
onSaveInstanceState
иonRestoreInstanceState
вы также можете использоватьViewModel
. Сделайте свою модель данных расширеннойViewModel
, и тогда вы сможете использоватьViewModelProviders
для получения одного и того же экземпляра вашей модели каждый раз при повторном создании действия:Чтобы использовать
ViewModelProviders
, добавьте следующееdependencies
вapp/build.gradle
:Обратите внимание, что вы
MyActivity
расширяете,FragmentActivity
а не просто расширяетеActivity
.Вы можете прочитать больше о ViewModels здесь:
источник
ViewModel
Это особенно удобно, если у вас есть большие наборы данных, которые нужно сохранить при изменении состояния, например при повороте экрана. Я предпочитаю использоватьViewModel
вместо записи его,Application
потому что он четко определен, и я могу иметь несколько операций одного приложения, которые ведут себя правильно.Я обнаружил, что этот ответ вызывает некоторые сбои в версиях Android 9 и 10. Я думаю, что это хороший подход, но когда я посмотрел код Android, то обнаружил, что в нем отсутствует конструктор. Ответ довольно старый, поэтому в то время, вероятно, в этом не было необходимости. Когда я добавил отсутствующий конструктор и вызвал его от создателя, сбой был исправлен.
Итак, вот отредактированный код:
источник
Для дополнения других ответов - если у вас есть несколько пользовательских составных представлений с одним и тем же идентификатором, и все они восстанавливаются с состоянием последнего представления об изменении конфигурации, все, что вам нужно сделать, это указать представлению только отправлять события сохранения / восстановления. к себе, переопределяя пару методов.
Для объяснения того, что происходит и почему это работает, смотрите этот пост в блоге . В основном идентификаторы дочерних видов вашего составного представления совместно используются каждым составным представлением, и восстановление состояния становится запутанным. Отправляя только состояние для самого составного представления, мы запрещаем их дочерним элементам получать смешанные сообщения из других составных представлений.
источник
Легко с Kotlin
источник