I want my app to support different layouts for right-handed and left-handed users. So after changing the respective preference I want to restart the activity in the same way as it restarts when e.g. orientation changes.
What I tried so far:
1.
Intent intent = getIntent();
finish();
startActivity(intent);
This does not store and load the saved instance state
2.
View cv = findViewById(android.R.id.content);
SparseArray<Parcelable> state = new SparseArray<Parcelable>();
cv.saveHierarchyState(state);
setContentView(desiredCv);
cv = findViewById(android.R.id.content);
cv.restoreHierarchyState(state);
Even then many things aren't as they should be.
I think that in the end I could figure out how to change layout properly without restarting but it would be much easier to do it in the same way as for system-defined configuration changes.
You could use Fragments and do it programmatically. Following the same way you could also rearrange dinamically your elements in the UI but I think it would be complicated to maintain.
OnSaveInstanceState() is not called on an Activity being finish-ed. And I'm not aware of a way to let Android handle this for you.
The solution may be to create 2 different layout files. Then you programmatically select the right one in the onCreate() method based on the preference value that has been updated.
If your two layouts use the same ids for those views that you need to restore, then you can implement two methods that save these states in a Bundle and retrieve these states from the Bundle. When you want to change layout, start a new activity with the proper intent (telling the activity which layout to load); in the bundle associated with this intent save what you need and retrieve it in onCreate() to update the views. You don't need to duplicate the code in the case you use layout dx or sx; you can use the same code since the two layouts use the same ids.
In this way you still have your one activity and can reuse your code. And the same code you use to restore the state between layout changes can still be used with onSaveInstanceState() and onRestoreInstanceState(), since, again, the ids are the same.
To make it short, use bundles.
If you are using API level 11 or higher (Android 3.0 or later), you can call recreate() in your activity. This will destroy the current instance and create a new one and should do the same thing as what happens during a configuration change (ie onSaveInstanceState() will be called, etc.).
Related
I have a fragment, which contains many EditTexts.
And when I rotate the device the EditText goes blank. The fragment is not saving its state/value. But at the same time if I use that fragment layout for activity it stores the EditText's state.
And yes I've given the IDs to each EditText, even to each view if that matters.
I know I can use saveInstanceState to save those values but is there any other way to do it? Cause there are almost 20 EdiText in that fragment, so should I use saveInstanceState, will it be okay to save these many variables/values in saveInstanceState?
Update:
I was recreating the fragment in activity on orientation change, so that was the reason, EditText was unable to save its state.
Such a silly mistake!
So now I just used saveInstanceState like following:
if (savedInstanceState == null) {
initialiseNewTaskFragment();
}
And that's it. EditText is saving its state now.
Thank you JorgeGil for saving my time!
Well this is a fairly loaded question lol.
So let's start with a few points.
First, you can of course handle your own lifecycle change if you choose to NOT allow Android to reset your lifecycle on device rotate.
android:configChanges="orientation"
Use that flag in your manifest if you want to retain everything and handle your own rotation changes. However, if you have a layout-land folder with different XML files, you will not want to do this.
Yes of course you can do fragment.retainInstance when nested in Activities to get it to retain values. However, retaining populated Elements with values is not something you can just natively expect it to do as the UI elements were completely redrawn, so something has to tell it to redraw it again.
So if you are going old school and you are actually still doing findViewById and myText.setText('some Stuff'). Then you may find some time savings in using a library like icepick.
https://github.com/frankiesardo/icepick
However, the BEST option by a mile is to modernize your coding practice to use DataBinding. This allows you to not care about the UI interactions anymore as the values are bound to your Fragment or Activity or Model values by default and can be done with 2-way binding. This ensures databinding always populates with the value that was last updated.
Imagine you have an object of
public class Student implements BaseObservable{
String firstName;
}
Then in your xml you have
editText
android:text="#={student.firstName}"
Obviously there is a little more, like you need to set your student object in the onCreate to ensure it is in the XML for using. But when the user modifies the student firstname it is retained in the model, and redrawn automatically into the Edit Text.
This is your best solution, but depends on how invested you are in the future binding techniques of Android development or if you just prefer to go status quo to get across a finish line.
Hope that helps.
Add this in your Activity tag on your AndroidManifest.xml to avoid the recreation of the Activity and you won't lose the data.
android:configChanges="orientation|screenSize"
The Android Developer Guide states that activities are launched via Intents:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
For Fragments, the usual way to display it on the screen is as follows:
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
Why is it that in one case I have to specify a class, and in the other an object? I.e., I would like to use something like
Activity nextActivity = new SignInActivity();
Intent intent = new Intent(this, nextActivity);
startActivity(intent);
Because Activity lifecycle is managed by Android whereas Fragment lifecycle is tied to the Activity in which it is contained.
As mentioned, Activity lifecycle is managed by Android. This is required, among other things, for Android to manage the system resources and also to take care of the back stack.
Fragment, on the other hand, was introduced to modularize and better organize the UI for devices with different sizes. According the the documentation:
Starting with HONEYCOMB, Activity implementations can make use of the
Fragment class to better modularize their code, build more
sophisticated user interfaces for larger screens, and help scale their
application between small and large screens.
To answer the latter part of your question, you can indeed pass the results of an activity to a second activity. But you should never create an instance of an Activity class for that. The right way is to use the startActivityForResult() and send the resulting value to the destination activity through the Intent.
While adding fragment, you are already specifying where exactly to insert that fragment into. So, the ideal way is to,
Create your fragment.
Insert into a layout of your current activity.
Use transactions to remove/manage your fragments, added to the current activity.
In no way, you could launch or use just a fragment, without attaching it to an existing activity.
Android handles Activity life cycle by itself. Just look at the methods of Activity class, they're just like a fill in the blanks. Android calls the shots here. Through these methods it just ask if you want to do something when this activity is created, resumed, paused etc.
The reasons for Android handling activity life cycle internally, are many:
Properly setting up an Activity involves lots of boiler plate code, better let system do it for you. The whole Context and window management is set up for you behind the scenes. Imagine the amount of extra work, if you had to do it for every Activity you created.
Activities are shared, home screen and other applications might want to launch/use them. How would be this possible if they have to call new MyActivity() of some obscure package ? . This is why Activities and other externally invokable components must be declared in application manifest.
Activities from many applications can be parts of an android task ( a piece of work from user's perspective). And are automatically placed/removed/re-arranged on a back-stack. Again, its better Android manage their creation and destruction rather than developers messing with this whole setup.
All user cares is that an Activity must show up when asked for, and just get out of the way if user navigates somewhere else. Android enforces this. Making an Activity appear on its own, or refuse to go away, just because its allowed to be programmed that way, is unacceptable.
Now Fragments , on the other hand are internal. They live inside an Activity and are not accessed from or shared with outside applications or tasks in any way. Fragments are even not a part of application manifest and hence are not exposed outside. Android need not worry about each fragment separately, because fragment life-cycle is bound to that of its parent Activity. Android doesn't care what you do with fragments internally, it can just end the activity and everything inside it is destroyed as well.
I have activity the contains a ListView, and this listview is filled with data from a web service in onCreate() function ( using AsyncTask). When the screen orientation changes the activity is re-created, I don't want that. how to keep saved after changing the orientation
You need to add this in the Activity in AndroidManifest.xml. Just adding "orientation" will not work on all devices.
android:configChanges="orientation|screenSize"
Set android:configChanges="orientation" in your AndroidManifest.xml for the activity.
Although the other answers suggest using android:configChanges, I would say this is really only a last resort option because it means resources for different orientations will not get loaded automatically.
Instead you should use the onSaveInstanceState or onRetainNonConfigurationInstance functions of your activity. (If you use fragments, you should use setRetainInstance instead of the latter option.)
Another option would be to use a ContentProvider to store the data you download using the AsyncTask into a database so it wouldn't need to be re-downloaded anyway.
You should read this for more information.
I programmatically create a RelativeLayout with some other views inside it and add it to the parent which is defined in XML. But all the views (incl. the layout) that were created programmatically disappear after the activity is re-created. Do I need a SharedPreferences object to save the values and then re-create the layout or is there an easier way to save it?
P.S. all new created views get an id assigned
Do I need a SharedPreferences object to save the values and then re-create the layout
You say that you are creating these widgets in onResume(). Your code in onResume() needs to know what widgets need to be created. If this is purely transient information, and you are worried about things like the activity being destroyed and re-created due to a screen rotation or other configuration change, use onSaveInstanceState() on the activity or fragment to pass data about these widgets from the old to the new instance of the activity. If, on the other hand, you are worried about being able to re-create these views several months later, you need to store this information persistently: SharedPreferences, database, some other file structure, "the cloud", etc.
I recommend you look into Activity.onSaveInstanceState(Bundle):
The default implementation takes care of most of the UI per-instance state for you by calling onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState()). If you override this method to save additional information not captured by each individual view, you will likely want to call through to the default implementation [ via super.onSaveInstanceState() and super.onRestoreInstanceState()], otherwise be prepared to save all of the state of each view yourself.
https://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)
as well as my answer here:
How can I assign an ID to a view programmatically?
I have Activity with ListView inside it and in the onCreate method of the Activity I have code for populating the Data of the ListView this Data is a server based and so populating includes calling Network URLs. I have the ArrayAdapter of the ListView in the Same Activity Class.
Now the Issue I'am facing is that, in Rest all scenarios my Activity is behaving in a proper way but when the Orientation [ Portrait to Landscaped or other way round] is taking place the Data is Getting lost and Newer Data calls are Required to Populate the Same Old Data now this is something that is not intended out the code how should I deal with it.
Android will stop and restart your activity unless you've told it you will handle orientation changes yourself. It does this so that you can specify different resources (such as layouts) that are orientation-specific (among other reasons).
You need to add android:configChanges="orientation" to your activity delcaration in your AndroidManifest.xml, and you need to override onConfigurationChanged(). I don't believe you need to do anything special inside onConfigurationChanged(), simply implementing it should do the trick.
For those targeting API 13 or higher, "screenSize" should also be used. If that is your case, add
android:configChanges="orientation|screenSize"
to your Android manifest.
More information here.
AFAIK the whole Activity gets recreated on an orientation change!
The same is true if you switch to anoter app and return back later.
I would suggest to store the data is the SharedPreferences or serialize them into XML and store them.
Another possibility could be to register your own service that stores the data in memory and the activity poluplates the data from the service.
Why not saving any data into a parceable and if the bundle you get in onCreate contains a saved state of data re-set the list adapter? Here is a nice code sample on this: http://blog.cluepusher.dk/2009/10/28/writing-parcelable-classes-for-android/
android:configChanges="orientation|screenSize"
this code will work only if we don't have another layout for landscape mode!