Bra*_*ein 243 android state screen-orientation android-activity
我已经成功实现onRetainNonConfigurationInstance()
了我的主要功能,Activity
以保存和恢复屏幕方向更改中的某些关键组件.
但看起来,当方向发生变化时,我的自定义视图将从头开始重新创建.这是有道理的,虽然在我的情况下它很不方便,因为所讨论的自定义视图是X/Y图,并且绘制的点存储在自定义视图中.
是否有一种狡猾的方法来实现类似于onRetainNonConfigurationInstance()
自定义视图的东西,或者我是否需要在自定义视图中实现允许我获取并设置其"状态"的方法?
Kob*_*r42 453
我认为这是一个更简单的版本.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);
}
}
Run Code Online (Sandbox Code Playgroud)
Ric*_*ler 408
您可以通过执行做到这一点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];
}
};
}
}
Run Code Online (Sandbox Code Playgroud)
该工作分为View和View的SavedState类.你应该Parcel
在SavedState
课堂上做所有的阅读和写作工作.然后,您的View类可以完成提取状态成员的工作,并完成将类恢复到有效状态所需的工作.
注意:如果返回值> = 0 View#onSavedInstanceState
,View#onRestoreInstanceState
则会自动为您View#getId
调用setId
.当您在xml中为其指定id或手动调用时,会发生这种情况.否则,您必须调用View#onSaveInstanceState
并写入Parcelable返回给你的包裹Activity#onSaveInstanceState
,保存状态,然后阅读并把它传递给View#onRestoreInstanceState
从Activity#onRestoreInstanceState
.
另一个简单的例子是 CompoundButton
Blu*_*ell 18
这是使用上述两种方法的混合的另一种变体.结合速度和正确性Parcelable
与简单性Bundle
:
@Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
// The vars you want to save - in this instance a string and a boolean
String someString = "something";
boolean someBoolean = true;
State state = new State(super.onSaveInstanceState(), someString, someBoolean);
bundle.putParcelable(State.STATE, state);
return bundle;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
State customViewState = (State) bundle.getParcelable(State.STATE);
// The vars you saved - do whatever you want with them
String someString = customViewState.getText();
boolean someBoolean = customViewState.isSomethingShowing());
super.onRestoreInstanceState(customViewState.getSuperState());
return;
}
// Stops a bug with the wrong state being passed to the super
super.onRestoreInstanceState(BaseSavedState.EMPTY_STATE);
}
protected static class State extends BaseSavedState {
protected static final String STATE = "YourCustomView.STATE";
private final String someText;
private final boolean somethingShowing;
public State(Parcelable superState, String someText, boolean somethingShowing) {
super(superState);
this.someText = someText;
this.somethingShowing = somethingShowing;
}
public String getText(){
return this.someText;
}
public boolean isSomethingShowing(){
return this.somethingShowing;
}
}
Run Code Online (Sandbox Code Playgroud)
Zak*_*nov 14
使用 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
}
}
Run Code Online (Sandbox Code Playgroud)
这里的答案已经很好了,但不一定适用于自定义ViewGroups.要使所有自定义视图保留其状态,您必须覆盖onSaveInstanceState()
并onRestoreInstanceState(Parcelable state)
在每个类中.您还需要确保它们都具有唯一ID,无论它们是从xml中膨胀还是以编程方式添加.
我想出的非常像Kobor42的答案,但错误仍然存在,因为我是以编程方式将Views添加到自定义ViewGroup而不是分配唯一ID.
mato共享的链接可以工作,但这意味着没有一个单独的视图管理它们自己的状态 - 整个状态保存在ViewGroup方法中.
问题是当这些ViewGroup中的多个被添加到布局时,来自xml的元素的id不再是唯一的(如果它在xml中定义).在运行时,您可以调用静态方法View.generateViewId()
来获取View的唯一ID.这仅适用于API 17.
这是来自ViewGroup的代码(它是抽象的,mOriginalValue是一个类型变量):
public abstract class DetailRow<E> extends LinearLayout {
private static final String SUPER_INSTANCE_STATE = "saved_instance_state_parcelable";
private static final String STATE_VIEW_IDS = "state_view_ids";
private static final String STATE_ORIGINAL_VALUE = "state_original_value";
private E mOriginalValue;
private int[] mViewIds;
// ...
@Override
protected Parcelable onSaveInstanceState() {
// Create a bundle to put super parcelable in
Bundle bundle = new Bundle();
bundle.putParcelable(SUPER_INSTANCE_STATE, super.onSaveInstanceState());
// Use abstract method to put mOriginalValue in the bundle;
putValueInTheBundle(mOriginalValue, bundle, STATE_ORIGINAL_VALUE);
// Store mViewIds in the bundle - initialize if necessary.
if (mViewIds == null) {
// We need as many ids as child views
mViewIds = new int[getChildCount()];
for (int i = 0; i < mViewIds.length; i++) {
// generate a unique id for each view
mViewIds[i] = View.generateViewId();
// assign the id to the view at the same index
getChildAt(i).setId(mViewIds[i]);
}
}
bundle.putIntArray(STATE_VIEW_IDS, mViewIds);
// return the bundle
return bundle;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
// We know state is a Bundle:
Bundle bundle = (Bundle) state;
// Get mViewIds out of the bundle
mViewIds = bundle.getIntArray(STATE_VIEW_IDS);
// For each id, assign to the view of same index
if (mViewIds != null) {
for (int i = 0; i < mViewIds.length; i++) {
getChildAt(i).setId(mViewIds[i]);
}
}
// Get mOriginalValue out of the bundle
mOriginalValue = getValueBackOutOfTheBundle(bundle, STATE_ORIGINAL_VALUE);
// get super parcelable back out of the bundle and pass it to
// super.onRestoreInstanceState(Parcelable)
state = bundle.getParcelable(SUPER_INSTANCE_STATE);
super.onRestoreInstanceState(state);
}
}
Run Code Online (Sandbox Code Playgroud)