I have an Android app, that allows the user to dynamically add their own buttons to the layout. I need to make it so that once the app is closed and re-opened, this dynamically added button returns to the layout. Instead of loading the default layout.
Currently, I'm dynamically adding buttons through the ActionBar of the App:
if (id == R.id.add_button)
{
String string = "Adding Button in Progress";
Toast.makeText(getApplicationContext(), string, Toast.LENGTH_SHORT).show( );
Button myButton = new Button(this);
myButton.setText("Button");
RelativeLayout layout = (RelativeLayout)findViewById(R.id.Layout1);
layout.addView(myButton);
//myButton.setVisibility(View.VISIBLE);
return true;
}
This creates the Button fine, however when the app is closed and re-opened, it launches up the default layout.
I've done some research on having the app save and reload the updated layout. It seems that I need to use onSaveInstanceState. Here is what I have so far in terms of trying to save the layout:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the app state here:
savedInstanceState.putAll(savedInstanceState);
super.onSaveInstanceState(savedInstanceState);
}
And here is what I have in terms of trying to "reload/restore" said layout. Notice I'm not using onRestoreInstanceState, instead I'm doing it through the onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
//savedInstanceState.get(savedInstanceState);
}
else
{
//initialize members with default values for a new instance
setContentView(R.layout.activity_main);
}
}
I'm not sure if I'm saving/loading correctly, but any advice on how I can accomplish this task would be greatly appreciated.
Thanks for your time!
P.S. I'm still a fairly new member so I couldn't comment/ask questions on existing threads.
I know there's a lot of information out there on trying to load/save layouts, however in my case I need it to save the Button, and not a string of user text. In other words, its not a fixed value that I need to save. If the user adds n buttons, when the app is exited and relaunched, it should have those same 3 buttons.
Saved Instance State is a key-value pair about your activity. The documentation clearly says that it is destroyed when the app is closed .(pressing back button or if the system itself shutsdown the app).This is only useful when you are navigating within the app or changing orientation.
One solution is to create a Shared Preference of the details your application needs to identify the given structure consisting the dynamic contents. The fetch the values whenever you open the app and code according to it.
Other solutions are to use databases or files to store data about your dynamic content.
just store the String or Boolean.. when ever you try to use them in onRestoreInstanceState then create Button or anything dynamically and use stored String or boolean to set text on them
Related
I have got an activity where the user can enter host name, user name and password and then click on a "Verify credentials" button. Then the credentials will be checked, which will take some time. In the meantime the user should neither be able to change the credentials nor to click on "Verify" again. So, a modal dialog like the ProgressDialog seems perfect for this.
Unfortunately, ProgressDialog has the well-know limitations regarding orientation changes etc. The guide (UI/Dialogs) tells to avoid ProgressDialog at all and use a ProgressBar in the layout instead (like in Progress & Activity). What does this mean? Shall I create another activity with just one progress bar? Or disable all input fields and put a progress bar on top of it? Sounds quite weird to me... whats your preferred solution?
Best thing which I use is:
Put a ProgressBar just beside the Login Button.
I have put a progressbar beside it(Whose visibility is set to View.GONE) in the OnCreate method.
When the user clicks on the Login/Submit button, I set the visibility of the button to View.GONE and visibility of ProgressBar to View.VISIBLE.
It looks good and the user cannot click on the button until the work is done, If an error occurs, toggle the visibility to let the user try again
Like #micro.pravi mentioned in his answer, you can implement the ProgressBar inside your layout. To keep the state after an orientation change you have to use onSaveInstanceState and onRestoreInstanceState to save and restore important values, i.e. private variables, like the private boolean isChecking
public class MyActivity extends Activity {
public boolean isProcessing;
#Override
public void onCreate(Bundle stateBundle) {
super.onCreate(stateBundle);
// Set Layout
setContentView(R.layout.main);
if(stateBundle!=null) {
// read your data here from the bundle
isProcessing = stateBundle.getBoolean("isProcessing");
}
setUiState(isChecking);
}
#Override
protected void onRestoreInstanceState(Bundle stateBundle) {
// Second value of getBoolean is the default value
isProcessing = stateBundle.getBoolean("isProcessing", false);
super.onRestoreInstanceState(stateBundle);
}
#Override
protected void onSaveInstanceState(Bundle stateBundle) {
// Save the critical data
stateBundle.putString("isProcessing", isProcessing);
super.onSaveInstanceState(stateBundle);
}
#Override
protected onResume() {
setUiState(isProcessing);
}
private setUiState(boolean processing) {
textView.setEnabled(!processing);
button.setEnabled(!processing);
progressbar.setVisibility(processing?View.VISIBLE:View.GONE);
}
}
This should be used to saved any critical data on orientation change or when the App is being killed and later restored by the OS. You don't have to save your TextView data, as the defautl View elements already handle this by themselves. Also do not store Image data this way. Instead store the Uri or path to the Url and load it on restore
For temporarily solving your problem, you can continue using the Progress Dialog and put this line in your Login Activity's tag in Manifest.xml file :
android:configChanges="orientation|keyboardHidden|screenSize"
Using this line of code will not affect the Progress Dialog on orientation changes. But it is considered a wrong practice according to Android Development's Documentation.
In the long run, i recommend you to Preserve the states for orientation changes.
I am inflating a view on button click and the user can add as many views as he likes, all is fine I made it work, but now the problem is when I go back one activity and come again to my dynamically generated activity every single view that was generated is gone. Similar is the case if I go to next activity and come back to the inflated activity. I know about onSaveInstance and onRestoreSaveInstance. But how do I put view information in a bundle in onSaveInstanceState? Please note that my view was generated Dynamically i.e. on button Click and I want to know as of how to preserve the state of my activity.
How do you go about it?
I am thinking that you should implement some kind of logic that helps you restore the state of your Views. So you should be designing a class, let say ViewDetail that somehow keeps details about the Views that you are adding.... type, dimension, etc. This class should implement Parcelable so you are able to add it to the bundle.
So you will keep an ArrayList<ViewDetail>, myViews where everytime the user adds a new View you create a new ViewDetail object that you add to your myViews array.
And then save your Views and restore them using those objects:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//save your view states
outState.putParcelableArrayList("MY_VIEWS",myViews);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//get the views back...
myViews=savedInstanceState.getParcelableArrayList("MY_VIEWS");
//TODO: add the views back to your Activity
}
As your application may be killed completely at any moment without noticem you have to provide long term storage off heap memory
You only have to restore all the views, if your activity was terminated (and it can be at any time). When it is activated again after termination, it goes through onCreate() method
- this would be proper place to restore activity state.
Only callback which is guaranted to be called before your application / activity is destroyed is onPause() - this is a proper place to save views states into long term off-heap storage.
I have seen the following links before posting this question
http://www.devx.com/wireless/Article/40792/1954
Saving Android Activity state using Save Instance State
http://www.gitshah.com/2011/03/how-to-handle-screen-orientation_28.html
How to save state during orientation change in Android if the state is made of my classes?
I am not getting how should i override the following function :
#Override
public Object onRetainNonConfigurationInstance() {
return someExpensiveObject;
}
In my application i have layout with one editext visible and other editext get visible when the data of first editext validates to true.I have set the visbility of all other editextes and textviews to false and make them visible after validating.
So in my activity if the screen orientation is changed then all the items having android:visibility="false" get invisible.
I have also came to know that when our activities screen orientation changes it calls onStop() followed by onDestroy() and then again starts a fresh activity by calling onCreate()
This is the cause .. But i am not getting how to resolve it ..
Here You can see the screenshots of my application :
in this image all fields are loaded
and in another image when the screen orientation is changed to landscape they are all gone
Any link to tutorial or piece of code will be highly appreciable.
And also my application crashes when a progress dialog is shown up and i try to change screen orientation.How to handle this ??
Thanks
Well if you have the same layout for both screens then there is no need to do so just add below line in your manifest in Activity node
android:configChanges="keyboardHidden|orientation"
for Android 3.2 (API level 13) and newer:
android:configChanges="keyboardHidden|orientation|screenSize"
because the "screen size" also changes when the device switches between portrait and landscape orientation.
From documentation here: http://developer.android.com/guide/topics/manifest/activity-element.html
There is another possibility using which you can keep the state as it is even on Orientation change using the onConfigurationChanged(Configuration newConfig).
Called by the system when the device configuration changes while your activity is running. Note that this will only be called if you have selected configurations you would like to handle with the configChanges attribute in your manifest. If any configuration change occurs that is not selected to be reported by that attribute, then instead of reporting it the system will stop and restart the activity (to have it launched with the new configuration).
At the time that this function has been called, your Resources object will have been updated to return resource values matching the new configuration.
There are 2 ways of doing this, the first one is in the AndroidManifest.xml file. You can add this to your activity's tag. This documentation will give you an in depth explanation, but put simply it uses these values and tells the activity not to restart when one of these values changes.
android:configChanges="keyboardHidden|orientation|screenSize|screenLayout"
And the second one is: overriding onSaveInstanceState and onRestoreInstanceState. This method requires some more effort, but arguably is better. onSaveInstanceState saves the values set (manually by the developer) from the activity before it's killed, and onRestoreInstanceState restores that information after onStart() Refer to the official documentation for a more in depth look. You don't have to implement onRestoreInstanceState, but that would involve sticking that code in onCreate().
In my sample code below, I am saving 2 int values, the current position of the spinner as well as a radio button.
#Override
public void onSaveInstanceState(#NonNull Bundle savedInstanceState) {
spinPosition = options.getSelectedItemPosition();
savedInstanceState.putInt(Constants.KEY, spinPosition);
savedInstanceState.putInt(Constants.KEY_RADIO, radioPosition);
super.onSaveInstanceState(savedInstanceState);
}
// And we restore those values with `getInt`, then we can pass those stored values into the spinner and radio button group, for example, to select the same values that we saved earlier.
#Override
public void onRestoreInstanceState(#NotNull Bundle savedInstanceState) {
spinPosition = savedInstanceState.getInt(Constants.KEY);
radioPosition = savedInstanceState.getInt(Constants.KEY_RADIO);
options.setSelection(spinPosition, true);
type.check(radioPosition);
super.onRestoreInstanceState(savedInstanceState);
}
How do I retain the state of an activity in android? I have two layouts for portrait and landscape in layout and layout-land. I am loading the value from service at the time I am showing progress dialog. If loaded user rotates the device to landscape at the time also loading. How do I avoid that? user typed content in webview that also refreshed. How do I avoid that, can anybody provide an example?
Thanks
When orientation changes, the Activity is reloaded by default. If you do not want this behavior then add this to the Activity definition in your manifest:
android:configChanges="orientation|keyboardHidden"
For more detail, see Handling Runtime Changes
You can use the onRetainNonConfigurationChange() callback to store arbitrary data. It is called just before your application is about to be recreated.
Then, in onCreate() just check if some data were put aside by calling getLastNonConfigurationInstance() that returns the Object you put aside or null.
See this article on android developers.
Here's a sample borrowed from the link above:
#Override
public Object onRetainNonConfigurationInstance() {
//this is called by the framework when needed
//Just return what you want to save here.
return MyBigObjectThatContainsEverythingIWantToSave;
}
Automagic restore of previously saved state:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final MyDataObject MyBigObjectThatContainsEverythingIWantToSave = (MyDataObject) getLastNonConfigurationInstance();
if (MyBigObjectThatContainsEverythingIWantToSave == null) {
//No saved state
MyBigObjectThatContainsEverythingIWantToSave = loadMyData();
} else {
//State was restored, no need to download again.
}
...
}
When orientation changes, the Activity is reloaded by default. If you do not want this behavior then add this to the Activity definition in your android manifest file :
android:configChanges="orientation|screenSize|keyboardHidden"
Is there an built in way to save the contents of a listView as part of onSaveInstanceState to then restore later? I want the listView to look the same if the user hit the back button and now onCreate is being called again.
If you set your activity's launchMode to singleTask, then (unless the application was terminated / gc called upon) your data (list) will be preserved.
This way your device will hold only one running instance of your application at a time, so when you "launch it again" no matter from where, if it's already running in the background, then that instance will show up (with the latest data).
If there is a risk that your application was finished, and you still need the latest list of data to show up, this solution won't work.
But you could give a try to SharedPreferences: save the current data to the application's SharedPreferences, and restore it from there when launching it.
If it's ok, to have the predefined new list on each clean start of the application, but when getting it into foreground, you need the last seen items in your list, you should use the savedInstanceState parameter of your onCreate method:
private static final String MYLISTKEY = "myListLabels";
private ArrayList<String> listLabels = new ArrayList<String>();
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState.containsKey(MYLISTKEY))
{
listLabels = savedInstanceState.getStringArrayList(MYLISTKEY);
}
else
{
// TODO: populate explicitely your list
}
}
#Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putStringArrayList(MYLISTKEY, listLabels);
}
where listLabels contains the labels for your list.
It's not necessary for them to be of type String, you can put any type inside your Bundle.
When the user hits the back button the activity is always destroyed, so there will not be any restoring from savedinstance.
Android Training
When your activity is destroyed because the user presses Back or the activity finishes itself, the system's concept of that Activity instance is gone forever because the behavior indicates the activity is no longer needed.