使用OnSaveInstanceState与OnRestoreInstanceState退出APP后无法恢复视图问题
Hey there! Let's break down exactly why your saved state isn't showing up when you reopen the app, and how to fix it.
The Core Issues
- You're only saving partial data: Your current
onSaveInstanceStatestores just one input string and a layout ID, but you're creating multiple CardViews with unique text, background colors, and sizes. None of that per-CardView detail is being saved. - You're not rebuilding the UI: Right now, you fetch values from the
BundleinonRestoreInstanceStatebut don't use them to recreate your CardViews/TextViews or add them back to the layout. Retrieving data without acting on it won't bring your UI back.
Step-by-Step Fix
1. Create a Data Class to Store CardView State
First, make a class to hold all the info needed to rebuild a CardView. We'll use Parcelable for efficient Bundle storage:
import android.os.Parcel; import android.os.Parcelable; public class CardViewState implements Parcelable { private String text; private int backgroundColor; private int width; private int height; public CardViewState(String text, int backgroundColor, int width, int height) { this.text = text; this.backgroundColor = backgroundColor; this.width = width; this.height = height; } protected CardViewState(Parcel in) { text = in.readString(); backgroundColor = in.readInt(); width = in.readInt(); height = in.readInt(); } public static final Creator<CardViewState> CREATOR = new Creator<CardViewState>() { @Override public CardViewState createFromParcel(Parcel in) { return new CardViewState(in); } @Override public CardViewState[] newArray(int size) { return new CardViewState[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(text); dest.writeInt(backgroundColor); dest.writeInt(width); dest.writeInt(height); } // Getters for saved data public String getText() { return text; } public int getBackgroundColor() { return backgroundColor; } public int getWidth() { return width; } public int getHeight() { return height; } }
2. Save All CardView States in onSaveInstanceState
Update your save method to collect data from every dynamically created CardView:
@Override public void onSaveInstanceState(Bundle savedInstanceState) { super.onSaveInstanceState(savedInstanceState); // Call super FIRST! // Collect state for all CardViews with your tag ArrayList<CardViewState> cardStates = new ArrayList<>(); for (int i = 0; i < mConstraintLayout.getChildCount(); i++) { View child = mConstraintLayout.getChildAt(i); if (child instanceof CardView && CARD_VIEW_TAG.equals(child.getTag())) { CardView card = (CardView) child; TextView tv = (TextView) card.getChildAt(0); CardView.LayoutParams params = (CardView.LayoutParams) card.getLayoutParams(); CardViewState state = new CardViewState( tv.getText().toString(), card.getCardBackgroundColor().getDefaultColor(), params.width, params.height ); cardStates.add(state); } } // Save the list to the Bundle savedInstanceState.putParcelableArrayList("card_states", cardStates); }
Pro tip: Always call super.onSaveInstanceState() before adding your own data—this ensures the system saves default view state first.
3. Restore CardViews in onCreate
Add logic to rebuild your CardViews when the app restarts:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initialize your views as before mContext = getApplicationContext(); mConstraintLayout = findViewById(R.id.top_left); addButton = findViewById(R.id.add); userInput = findViewById(R.id.editText); mButton = findViewById(R.id.create); // Restore saved CardViews if they exist if (savedInstanceState != null) { ArrayList<CardViewState> cardStates = savedInstanceState.getParcelableArrayList("card_states"); if (cardStates != null) { for (CardViewState state : cardStates) { recreateCardView(state); } } } // Your existing addButton and mButton click listeners go here... } // Helper method to rebuild a CardView from saved state private void recreateCardView(CardViewState state) { CardView sticker = new CardView(mContext); CardView.LayoutParams params = new CardView.LayoutParams( state.getWidth(), state.getHeight() ); sticker.setCardBackgroundColor(state.getBackgroundColor()); sticker.setId(CardView.generateViewId()); sticker.setTag(CARD_VIEW_TAG); sticker.setLayoutParams(params); // Reattach the long click listener (same as original code) sticker.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { ClipData.Item item = new ClipData.Item((CharSequence) sticker.getTag()); String[] mimeTypes = {ClipDescription.MIMETYPE_TEXT_PLAIN}; ClipData data = new ClipData(sticker.getTag().toString(), mimeTypes, item); View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(sticker); sticker.startDrag(data, shadowBuilder, sticker, 0); sticker.setVisibility(View.INVISIBLE); return true; } }); // Recreate the TextView TextView tv = new TextView(mContext); tv.setLayoutParams(params); tv.setBackgroundColor(Color.TRANSPARENT); tv.setGravity(Gravity.CENTER); tv.setText(state.getText()); tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); tv.setTextColor(Color.BLACK); sticker.addView(tv); mConstraintLayout.addView(sticker); }
4. Clean Up onRestoreInstanceState
You can remove your original onRestoreInstanceState method since we're handling restoration directly in onCreate. If you prefer to keep it, move the restoration logic there (just make sure to call super.onRestoreInstanceState(savedInstanceState) first).
Key Takeaways
- Save everything you need: Don't skip unique properties like color or size—if it makes your CardView distinct, it needs to be saved.
- Reuse code: The
recreateCardViewhelper keeps your code clean and avoids repetition between creating new CardViews and restoring old ones. - Follow super method order: Always call the superclass's save/restore methods first to avoid overriding system behavior.
内容的提问来源于stack exchange,提问作者matthausen

