I was looking at the bottom sheet behaviors and how to make it's state persists through destruction.
I found this class SavedState https://developer.android.com/reference/com/google/android/material/bottomsheet/BottomSheetBehavior.SavedState
but I could not find any example on how to use that.
Moreover, handling state persistence can just be handled through the bundle
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(Key, BottomSheetBehavior.from(yourscrollview).state)
}
private fun loadSavedInstanceState(savedInstanceState: Bundle) {
BottomSheetBehavior.from(yourscrollview).state = savedInstanceState.getInt(Key)
}
So what is that SavedState's purpose.
Whenever there is a configuration change such as screen rotation the activity is recreated.
In this recreation, the application gets restarted and may lose data in the views if not handled.
For do this there are two methods that are triggered at different stages of the lifecycle:
onSaveInstanceState
onRestoreInstanceState
They are used to save and retrieve values. The values are stored in the form of a key-value pair.
In your case:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(key, state);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
int state = savedInstanceState.getInt(key);
}
I'm wondering if there is a way to restore, after rotation, my list of results without retaining the fragment.
Basically I have a fragment which calls, through a presenter, some api (using RxJava and Retrofit). I added pagination so I can make a call only when I need more data scrolling down.
I'm in the following scenario:
- I scroll the list down in order to call the second page from the web
- I rotate the screen
In this case what I would need is to show all the items, from page 1 and 2, and then scrolling to the correct position (for this I can use the onSaveInstanceState of the LayoutManager).
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FwApplication.component(getActivity()).inject(this);
if (savedInstanceState != null) {
mRxRunning = savedInstanceState.getBoolean(EXTRA_RX);
mQuery = savedInstanceState.getString(QUERY_ARG);
mCurrentPage = savedInstanceState.getInt(CURRENT_PAGE);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(QUERY_ARG, mQuery);
outState.putInt(CURRENT_PAGE, mCurrentPage);
outState.putParcelable(LAYOUT_STATE, mRecyclerView.getLayoutManager().onSaveInstanceState());
super.onSaveInstanceState(outState);
}
#Override
public void onResume() {
super.onResume();
mPresenter.onResume(mRxRunning, mCurrentPage);
}
Is there a way to save the items without calling setRetainInstance? Moreover I would avoid to call the api in order to get all the items back.
The items are POJOs so it won't be a list of simple strings.
Try to edit the Manifest file. Add this line to block with activity:
android:configChanges="keyboardHidden|orientation|screenSize">
It will prevent Activity restart on such events (specially on orientation change)
I have a few CheckBox elements inside one of my Fragments.
Every time I leave this Fragment it seems to nor save or restore the checked state of each one provided by the user.
In the FragmentList example you can find:
CheckBox check1;
boolean active;
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("state1", check1.isChecked());
}
Which you can use later like this:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
// Restore last state for checked position.
check1.setChecked(savedInstanceState.getBoolean("state1"));
}
}
But somehow the CheckBox elements don`t save their state.
Is this the correct approach I should take?
Unless you're keeping your Fragment reference alive through the lifecycle of the application, it should be fine to save its state in onSaveInstanceState and restore it in onActivityCreated.
One important point, though, is also to save and restore that state in the Activity level by doing like:
public void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
// Restore the fragment's instance
mFragment = getSupportFragmentManager().getFragment(
savedInstanceState, "fragKey");
...
}
...
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Save the fragment's instance
getSupportFragmentManager().putFragment(outState, "fragKey", mContent);
}
Please check to see how your Activity is behaving in your scenario.
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
}
}
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
}
}