我已经成功地实现了onRetainNonConfigurationInstance()为我的主活动保存和恢复某些关键组件跨屏幕方向的变化。
但看起来,当方向改变时,我的自定义视图正在从头重新创建。这是有意义的,尽管在我的例子中不方便,因为所讨论的自定义视图是一个X/Y图形,并且绘制的点存储在自定义视图中。
是否有一种巧妙的方法来实现类似于onRetainNonConfigurationInstance()的自定义视图,或者我只需要在自定义视图中实现方法,允许我获得和设置其“状态”?
我已经成功地实现了onRetainNonConfigurationInstance()为我的主活动保存和恢复某些关键组件跨屏幕方向的变化。
但看起来,当方向改变时,我的自定义视图正在从头重新创建。这是有意义的,尽管在我的例子中不方便,因为所讨论的自定义视图是一个X/Y图形,并且绘制的点存储在自定义视图中。
是否有一种巧妙的方法来实现类似于onRetainNonConfigurationInstance()的自定义视图,或者我只需要在自定义视图中实现方法,允许我获得和设置其“状态”?
当前回答
用kotlin很容易
@Parcelize
class MyState(val superSavedState: Parcelable?, val loading: Boolean) : View.BaseSavedState(superSavedState), Parcelable
class MyView : View {
var loading: Boolean = false
override fun onSaveInstanceState(): Parcelable? {
val superState = super.onSaveInstanceState()
return MyState(superState, loading)
}
override fun onRestoreInstanceState(state: Parcelable?) {
val myState = state as? MyState
super.onRestoreInstanceState(myState?.superSaveState ?: state)
loading = myState?.loading ?: false
//redraw
}
}
其他回答
我发现这个答案在Android版本9和10上导致了一些崩溃。我认为这是一个很好的方法,但当我在看一些Android代码时,我发现它缺少一个构造函数。答案是相当古老的,所以在当时可能没有必要。当我添加了缺失的构造函数并从创建者调用它时,崩溃就修复了。
下面是编辑后的代码:
public class CustomView extends LinearLayout {
private int stateToSave;
...
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
// your custom state
ss.stateToSave = this.stateToSave;
return ss;
}
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container)
{
dispatchFreezeSelfOnly(container);
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
// your custom state
this.stateToSave = ss.stateToSave;
}
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container)
{
dispatchThawSelfOnly(container);
}
static class SavedState extends BaseSavedState {
int stateToSave;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
this.stateToSave = in.readInt();
}
// This was the missing constructor
@RequiresApi(Build.VERSION_CODES.N)
SavedState(Parcel in, ClassLoader loader)
{
super(in, loader);
this.stateToSave = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(this.stateToSave);
}
public static final Creator<SavedState> CREATOR =
new ClassLoaderCreator<SavedState>() {
// This was also missing
@Override
public SavedState createFromParcel(Parcel in, ClassLoader loader)
{
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? new SavedState(in, loader) : new SavedState(in);
}
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in, null);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
我有一个问题,onRestoreInstanceState恢复了我所有的自定义视图与最后一个视图的状态。我通过向我的自定义视图添加这两个方法来解决这个问题:
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
dispatchFreezeSelfOnly(container);
}
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
dispatchThawSelfOnly(container);
}
你可以通过实现view# onSaveInstanceState和view# onRestoreInstanceState并扩展视图来实现这一点。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类之间进行分割。您应该在SavedState类中完成对Parcel进行读写的所有工作。然后,您的View类可以执行提取状态成员的工作,并执行将类恢复到有效状态所需的工作。
注意:如果View#getId返回值>= 0,则View#onSavedInstanceState和View#onRestoreInstanceState将自动为您调用。当您在xml中给它一个id或手动调用setId时,就会发生这种情况。否则,您必须调用view# onSaveInstanceState并将Parcelable返回到您在活动#onSaveInstanceState中获得的包中以保存状态,随后读取它并将其传递给活动#onRestoreInstanceState中的view# onRestoreInstanceState。
另一个简单的例子是CompoundButton
我认为这是一个更简单的版本。Bundle是实现Parcelable的内置类型
public class CustomView extends View
{
private int stuff; // stuff
@Override
public Parcelable onSaveInstanceState()
{
Bundle bundle = new Bundle();
bundle.putParcelable("superState", super.onSaveInstanceState());
bundle.putInt("stuff", this.stuff); // ... save stuff
return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable state)
{
if (state instanceof Bundle) // implicit null check
{
Bundle bundle = (Bundle) state;
this.stuff = bundle.getInt("stuff"); // ... load stuff
state = bundle.getParcelable("superState");
}
super.onRestoreInstanceState(state);
}
}
除了使用onSaveInstanceState和onRestoreInstanceState,你还可以使用ViewModel。让你的数据模型扩展ViewModel,然后你可以在每次Activity被重新创建时使用ViewModelProviders来获得你的模型的相同实例:
class MyData extends ViewModel {
// have all your properties with getters and setters here
}
public class MyActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// the first time, ViewModelProvider will create a new MyData
// object. When the Activity is recreated (e.g. because the screen
// is rotated), ViewModelProvider will give you the initial MyData
// object back, without creating a new one, so all your property
// values are retained from the previous view.
myData = ViewModelProviders.of(this).get(MyData.class);
...
}
}
要使用ViewModelProviders,在app/build.gradle中添加以下依赖项:
implementation "android.arch.lifecycle:extensions:1.1.1"
implementation "android.arch.lifecycle:viewmodel:1.1.1"
注意你的MyActivity扩展了FragmentActivity而不仅仅是扩展了Activity。
你可以在这里阅读更多关于ViewModels的信息:
Android开发者指南,处理配置更改 Android开发者指南,保存UI状态,使用ViewModel处理配置更改 教程视图模型:一个简单的例子