I have 3 custom views.
The first one works great. It contains an EditText when I launch an intent and come back whatever the user entered is restored.
The 2nd contains a TextView and the 3rd a Spinner. They do not save when I launch my intent and return.
I think know how to preserve the data using onSaveInstanceState and onRestoreInstanceState in my custom views, However when the activity containing the custom views is not killed (meaning it is only paused), and I return onRestoreInstanceState is not called.
This is what I'm calling in my custom views when I need to save them.
#Override
public Parcelable onSaveInstanceState() {
textValue = editText.getText().toString();
Bundle bundle = new Bundle();
bundle.putParcelable("instanceState", super.onSaveInstanceState());
bundle.putString(TEXT_VALUE_KEY, this.textValue);
return bundle;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
textValue = bundle.getString(TEXT_VALUE_KEY);
editText.setText(textValue);
super.onRestoreInstanceState(bundle.getParcelable("instanceState"));
return;
}
super.onRestoreInstanceState(state);
}
I'm unsure I what I should do since onRestoreInstanceState is not called. I think the EditText customView works because default android behavior saved them temporarily, but it doesn't save spinners or TextViews.
You should change your onCreate() method in order to check if the Activity has already called onSavedInstanceState like this:
#Override
public void onCreate(Bundle savedInstanceState) {
if(savedInstanceState == null) {
// The activity haven't called onSavedInstanceState(), which means you should initialize your objects and UI here
}
else {
// Whatever states you saved onSavedInstanceState() are stored in savedInstaceState, so use them to reconstruct your customViews
}
}
Related
onSaveInstanceState and onRestoreInstanceState are not working. whenever i press back button or come out of the app the data of custom recyclerView is not visible until upload the next file. when i upload all the data comes back as I have stored it in shared preference.
Oncreate:
if (savedInstanceState != null) {
// Restore value of members from saved state
savedInstanceState.get(String.valueOf(savedInstanceState));
}
else
{
//initialize members with default values for a new instance
setContentView(R.layout.activity_resource);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
#Override
public void onRestoreInstanceState(#Nullable Bundle savedInstanceState, #Nullable PersistableBundle persistentState) {
super.onRestoreInstanceState(savedInstanceState, persistentState);
}
onSaveInstanceState stores view state automatically if all views inside layout has id.
For data restoring inside ListAdapter I would suggest sroring list inside ViewModel or at least Presenter depend on architecture you are using.
In my android app, I am trying to solve an issue with orientation change.
I have a main layout where I have two buttons. On click of the first button (default text on this button is "Select a category"), a dialog box appears with a category list with categories displayed as radio buttons. After the user selects a category, the selected category name appears on the button. Now when I change the orientation in the emulator, the Button text gets reset again.
I have used onSaveInstanceState() like below.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialization code
categoryList=(Button)findViewById(R.id.category_selection);
if (savedInstanceState != null)
{
System.out.println("savedInstanceState---
"+savedInstanceState.getString("bundle_category_name"));
categoryName=savedInstanceState.getString("bundle_category_name");
categoryList.setText(categoryName);
}
else
{
categoryList.setText(R.string.category);
}
// remaining code
}
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
// Save selected category name
System.out.println("saving category name "+categoryName);
outState.putString("bundle_category_name", categoryName);
}
I am able to get the category name back in onCreate(), the sysout prints correctly. But it is not getting set as the button text after change in orientation.
Please let me know if I am doing anything wrong.
Thanks
Add android:configChanges="orientation|screenSize" in your Android Manifest file.
<activity android:name="YourActivity"
...
android:configChanges="orientation|screenSize"
.../>
Saving and restoring the data works using two Activity lifecycle methods called onSaveInstanceState() and onRestoreInstanceState().
To save the state information override onSaveInstanceState() method and add key-value pairs to the Bundle object that is saved in the event that your activity is destroyed unexpectedly. This method gets called before onStop().
To recover your saved state from the Bundle override onRestoreInstanceState() method. This is called after onStart() and before onResume(). Check the below code
public class MainActivity extends Activity{
private static final String SELECTED_ITEM_POSITION = "ItemPosition";
private int mPosition;
#Override
protected void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
// Save the state of item position
outState.putInt(SELECTED_ITEM_POSITION, mPosition);
}
#Override
protected void onRestoreInstanceState(final Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Read the state of item position
mPosition = savedInstanceState.gettInt(SELECTED_ITEM_POSITION);
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
savedInstanceState.putBoolean("MyBoolean", true);
savedInstanceState.putDouble("myDouble", 1.9);
savedInstanceState.putInt("MyInt", 1);
savedInstanceState.putString("MyString", "back to Android");
// etc.
}
To Retrieve the data
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
double myDouble = savedInstanceState.getDouble("myDouble");
int myInt = savedInstanceState.getInt("MyInt");
String myString = savedInstanceState.getString("MyString");
}
My app needs to be able to dynamically create TextViews, so I need to be able to restore them if my app is ever GCed or switches orientations. The problem is I can't figure out how to restore the state of my TextViews. Saving their states with TextView.onSaveInstanceState() seems to work just fine, but when the Parcelable is passed to onRestoreInstanceState(), nothing happens and the resulting view is just blank. This is a short example which doesn't work:
public class MainActivity extends AppCompatActivity {
TextView v;
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_main);
ViewGroup main = (ViewGroup) findViewById(R.id.main);
v = new TextView(this);
main.addView(v);
if (state == null) {
v.setText("A simple message.");
} else {
v.onRestoreInstanceState(state.getParcelable("message"));
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("message", v.onSaveInstanceState());
}
}
I have checked the value of state.getParcelable("message") in the debugger and it definitely has the required information. onRestoreInstanceState() just isn't using it. Any help at all would be appreciated.
EDIT: I screwed up with the debugger. The information was actually never written to the bundle. TextView.onSaveInstanceState() had returned an empty Parcelable. That was the real problem.
I figured it out. It the problem was with TextView.onSaveInstanceState()—not TextView.onRestoreInstanceState(). TextView.onSaveInstaceState() only saves the state if you explicitly ask it to by calling TextView.setFreezesText(true) first. SURPRISE! Thank you, Google. Also note that onRestoreInstanceState() doesn't restore the FreezesText property. Oh, Google. You're just so zany. What'll we do with you?
Instead of restoring the state during onCreate() you may choose to implement onRestoreInstanceState(), which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null:
Save Your Activity State:
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
Restore Your Activity State:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance
}
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
I have a Fragment Activity with a FragmentTabHost. I add the fragments to the tab using the following code:
mTabHost.addTab(mTabHost.newTabSpec(tab1Name).setIndicator(tabIndicator1),
EventSettingsStep1Fragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec(tab2Name).setIndicator(tabIndicator2),
EventSettingsStep2Fragment.class, null);
When I switch to different tabs, I'd like to retain all the values (view state, etc) so that I have the same data when I switch back to the tab.
I overrode the onSaveInstanceState method & in there, I added values that I want retained to the bundle.
I ran through the methods being called and I have the following:
Switching from Tab1 to Tab2: Tab1:onPause then Tab2:onCreateView, Tab2:onResume
Switching from Tab2 to Tab1: Tab2:onPause then Tab1:onCreateView, Tab1:onResume
onSaveInstanceState is not being called.
Here is the code for one of my fragments:
public class EventSettingsStep1Fragment extends Fragment implements View.OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
if (savedInstanceState != null) {
Log.d(TAG, "restoring onSavedInstanceState");
Gson gson = new Gson();
event = gson.fromJson(savedInstanceState.getString("event"), Event.class);
}
if (event != null) {
//set views
}
return v;
}
#Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
#Override
public void onSaveInstanceState(Bundle outState) {
Log.d(TAG, "onSaveInstanceState");
super.onSaveInstanceState(outState);
Gson gson = new Gson();
outState.putString("event", gson.toJson(event));
}
}
Why is onSaveInstanceState not being called? Is it only triggered through the FragmentActivity?
onSaveInstanceState is not being called because the framework simply reuses the already-existing instance of the fragment. onSaveInstanceState only gets called when the instance is about to be destroyed and then recreated. This happens for example when you rotate the display and force the hosting activity to be recreated.
onSaveInstanceState is also not called when you push a fragment on the backstack of a FragmentManager. You will have to restore the state from the already existing instance, which can be very annoying. See SO questions How can I maintain fragment state when added to the back stack? and Once for all, how to correctly save instance state of Fragments in back stack? for example.
Basically you will have to do what the answers to these questions suggest: continue using the values of your instance variables and do not rely on a saved instance state.
I'm confused when it comes down to saving a state. So I know that onSaveInstanceState(Bundle) is called when the activity is about to be destroyed. But how do you store your information in it and bring it back to its original state in onCreate(Bundle savedInstanceState)? I don't understand how this bundle will restore information. It would be helpful if someone can provide an example.
The Dev guide doesn't do a good job of explaining this.
public class Conversation extends Activity {
private ProgressDialog progDialog;
int typeBar;
TextView text1;
EditText edit;
Button respond;
private String name;
private String textAtView;
private String savedName;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.dorothydialog);
text1 = (TextView)findViewById(R.id.dialog);
edit = (EditText)findViewById(R.id.repsond);
respond = (Button)findViewById(R.id.button01);
if(savedInstanceState != null){
savedInstanceState.get(savedName);
text1.setText(savedName);
}
else{
text1.setText("Hello! What is your name?");
respond.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
name = edit.getText().toString();
text1.setText("Nice to meet you "+ name);
}
});
}
}
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString(savedName, name);
}
}
The Bundle is a container for all the information you want to save. You use the put* functions to insert data into it. Here's a short list (there are more) of put functions you can use to store data in the Bundle.
putString
putBoolean
putByte
putChar
putFloat
putLong
putShort
putParcelable (used for objects but they must implement Parcelable)
In your onCreate function, this Bundle is handed back to the program. The best way to check if the application is being reloaded, or started for the first time is:
if (savedInstanceState != null) {
// Then the application is being reloaded
}
To get the data back out, use the get* functions just like the put* functions. The data is stored as a name-value pair. This is like a hashmap. You provide a key and the value, then when you want the value back, you give the key and the function gets the value. Here's a short example.
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("message", "This is my message to be reloaded");
super.onSaveInstanceState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String message = savedInstanceState.getString("message");
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
Your saved message will be toasted to the screen.
One major note that all new Android developers should know is that any information in Widgets (TextView, Buttons, etc.) will be persisted automatically by Android as long as you assign an ID to them. So that means most of the UI state is taken care of without issue. Only when you need to store other data does this become an issue.
From Android Docs:
The only work required by you is to
provide a unique ID (with the
android:id attribute) for each widget
you want to save its state. If a
widget does not have an ID, then it
cannot save its state
A good information: you don't need to check whether the Bundle object is null into the onCreate() method. Use the onRestoreInstanceState() method, which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null
Store information:
static final String PLAYER_SCORE = "playerScore";
static final String PLAYER_LEVEL = "playerLevel";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(PLAYER_SCORE, mCurrentScore);
savedInstanceState.putInt(PLAYER_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
If you don't want to restore information in your onCreate-Method:
Here are the examples: Recreating an Activity
Instead of restoring the state during onCreate() you may choose to implement onRestoreInstanceState(), which the system calls after the onStart() method. The system calls onRestoreInstanceState() only if there is a saved state to restore, so you do not need to check whether the Bundle is null
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance
mCurrentScore = savedInstanceState.getInt(PLAYER_SCORE);
mCurrentLevel = savedInstanceState.getInt(PLAYER_LEVEL);
}
Basically onSaveInstanceState(Bundle outBundle) will give you a bundle.
When you look at the Bundle class, you will see that you can put lots of different stuff inside it. At the next call of onCreate(), you just get that Bundle back as an argument.
Then you can read your values again and restore your activity.
Lets say you have an activity with an EditText. The user wrote some text inside it.
After that the system calls your onSaveInstanceState().
You read the text from the EditText and write it into the Bundle via Bundle.putString("edit_text_value", theValue).
Now onCreate is called. You check if the supplied bundle is not null. If thats the case,
you can restore your value via Bundle.getString("edit_text_value") and put it back into your EditText.
This is for extra information.
Imagine this scenario
ActivityA launch ActivityB.
ActivityB launch a new ActivityAPrime by
Intent intent = new Intent(getApplicationContext(), ActivityA.class);
startActivity(intent);
ActivityAPrime has no relationship with ActivityA.
In this case the Bundle in ActivityAPrime.onCreate() will be null.
If ActivityA and ActivityAPrime should be the same activity instead of different activities,
ActivityB should call finish() than using startActivity().
If Data Is not Loaded From savedInstanceState use following code.
The problem is url call is not to complete fully so, check if data is loaded then to show the instanceState value.
//suppose data is not Loaded to savedInstanceState at 1st swipe
if (savedInstanceState == null && !mAlreadyLoaded){
mAlreadyLoaded = true;
GetStoryData();//Url Call
} else {
if (listArray != null) { //Data Array From JsonArray(ListArray)
System.out.println("LocalData " + listArray);
view.findViewById(R.id.progressBar).setVisibility(View.GONE);
}else{
GetStoryData();//Url Call
}
}