Android custom component doesn't retain value on orientation change - android

I have a layout that uses TextView and a custom control I created that combine a TextView and two buttons for an integer up/down control. The TextView retains its value when the device is rotated, I'm calling the save/restore instance state. I'm not capturing any values as the device retains the values. However, this is not the case for my custom component.
Is there something I need to implement in a custom control so when the device orientation changes it retains the values like the native controls do?
Thank you.

Throw android:configChanges="orientation" into your activity tags in your AndroidManifest.xml file.

You can prevent your activity from being destroyed and recreated as Haphazard suggests, and this is probably the best solution.
If you don't want to do that, then you need override the following three methods:
isSaveEnabled() (override to return true)
onSaveInstanceState()
onRestoreInstanceState(Parcelable)
Consult the documentation (such as it is) for how to do this. There's also an example of how it's done here. The view will need a non-default ID to have it's onSaveInstanceState() called or else you'll also need to override onSaveInstanceState(Bundle) in your activity.

Related

Stop reload application when orientation changes

I need my app to not reload when the orientation changes. I have a regular portrait and a seperate landscape layout that both have the same stuff on the screen. But when I check a checkbox and change layout, it's no longer checked. Or when a checkbox has the number 5 in it and then change layout it goes back to it's default number 1. How can I make it stop reloading?
You need to use a way to persist data between the two screens.
Either use savedInstanceState or ViewModel.
The checked state of a CheckBox and text in an EditText will be automatically preserved if you make sure they have the same id in both layouts. However, it only automatically preserves the things that can be changed through the UI, so if you programmatically changed the text of a CheckBox, that is not automatically preserved. Typically, that sort of thing is handled with a ViewModel and LiveData/Flow.
This behavior is controlled by the saveEnabled property of a view, which is true by default.
add this following permission for your activity in Manifest file android:configChanges="orientation|screenSize
Something like this
<activity
android:name=".YourActvity"
android:configChanges="orientation|screenSize">
First tip from Philipp Lackner explains how to use the savedStateHandle
https://www.youtube.com/watch?v=0d5ax97Mb30
The state when you change orientation, for example, gets restored, so you need to handle via viewmodel the state persistence.

What is the advantage of letting an activity be destroyed on rotation?

I have used both approaches:
Let the activity be destroyed on rotation
Don't let the activity be destroyed on rotation
My approach almost everytime is to catch the rotation event and if needed call the setContentView and add some components again. If not, just let it rotate and the layouts are designed to adapt.
So far I only have seen advantages on letting it be destroyed on screens with very complex construction that are very dynamic, and whenever I rotate and not destroy show some flickering when re-building the screen.
The overhead of having to pass the state with onSaveInstance, onRestoreInstace is sometimes very error prone, and somehow time consuming.
Am I missing something?
UPDATE:
I'm not doing any kind of if "Orientation.XPTO == ..." on my code. This is the logic of each of the 2 approaches (the code is reused):
When destroying
onCreate -> DrawUI() setContentView and add views -> fill() add content
When not destroyed:
onCreate -> DrawUI() setContentView and add views -> fill() add content
onRotation -> DrawUI() setContentView and add views -> fill() add content
When calling setContentView after rotation it will pick the right layout for the device orientation (Check this answer by Google's Reto Meier https://stackoverflow.com/a/456918/327011 )
And the DrawUI and fill would have to have the logic for both the portrait and landscape layouts as the activity can be created on each of the two orientations to begin with.
Am I missing something?
Yes. You are assuming that your alternative is somehow less error prone.
By not going through the destroy-and-recreate cycle, you have to ensure that you are handling changing every resource for every possible configuration change.
Don't let the activity be destroyed on rotation
Unless you are using android:screenOrientation to force your activity into a single orientation (e.g., landscape), you cannot only handle rotation-related configuration changes. You need to handle all configuration changes. Otherwise, as soon as the user drops their device into a dock, removes it from a dock, changes language from Settings, attaches or detaches a keyboard, changes the global font scaling, etc., your app will break.
This, in turn, means that on every configuration change, you need to:
update your UI for your potentially new string resources
adjust or reload your layouts (and by "adjust" that includes changing any drawables, animations, menus, etc.)
anything else tied to your resources (e.g., array lists in your PreferenceFragment)
The problem is that you are going to forget something. For example, you will miss changing a string associated with an action bar item, so now most of your UI is in Spanish and that action bar item is in English. The sorts of things you are going to forget will be less obvious (how often do you test your Spanish translation?).
Your activity is destroyed to give you the opportunity to reconfigure yourself for the new orientation.
From the developer.android.com:
When the screen changes orientation, the system destroys and recreates
the foreground activity because the screen configuration has changed
and your activity might need to load alternative resources (such as
the layout).
For example, in landscape mode you may require a completely different layout, or may want to load in graphics that would not appear stretched. The best way of doing this is allowing the activity to be created again, which will allow the linking to the layout file to change to a more orientation-friendly layout.
See http://developer.android.com/training/basics/activity-lifecycle/recreating.html for more info and how to deal with the orientation change
If you want to disable the recreation you can add
android:configChanges="orientation"
to your Activity element in AndroidManifest.xml. This way your Activity will not be reloaded.
onSaveInstance and onRestoreInstace should only be used for passing through session information, for example the current text in a TextField, and nothing generic that can just be loaded in again after onCreate.
If you, restarting the Activity, requires recovering large sets of data, re-establishing a network connection, or perform other intensive operations then using the onSaveInstanceState() could potentially cause your noted symptoms:
A poor user experience (i.e. "show some flickering")
Require consumption of a lot of memory
onSaveInstanceState() callbacks are not designed to carry large objects.
To retain an object during a runtime configuration change:
Override the onRetainNonConfigurationInstance() method to return the object you would like to retain.
When your activity is created again, call getLastNonConfigurationInstance() to recover your object.
However:
While you can return any object, you should never pass an object that is tied to the Activity, such as a Drawable, an Adapter, a View or any other object that's associated with a Context. If you do, it will leak all the views and resources of the original activity instance. (Leaking resources means that your application maintains a hold on them and they cannot be garbage-collected, so lots of memory can be lost.)
Source
Unless you are able to pass the Object(s) smoothly I personally think it is more advantageous to handle the configuration change yourself, meaning not to destroy.
If you have a target API of 13 or higher: You must include screenSize in your configChanges. Starting with API 13 the screen size also changes on orientation change and you'll need to account for this. Prior to 13 your Activity would handle this itself.
android:configChanges="orientation|screenSize"
Some time it is useful when you are using different layouts for (Landscape / Portrait ). and using different type of views for example ListView in portrait and GridView in landscape.
I guess you are not considering the standard way of creating the android layouts. Please correct me If I'm wrong. Are you using two res folders with -port,-land separately to tell android system to choose in runtime to load the different assets and layout on the basis of orientation.
This example can give you a clue to manage layouts in different orientations.
Here is the android stanard document. Please check with "land" and "port".
Hope this will help you.

how to save state of activity

i am new to android.i have one single activity with main.xml file. Now, i have one scroll view in that main.xml file.when i run my application in portrait mode and when i go to the bottom of the scroll view and than when i change my application state to the landscape mode than i go to the top of the scroll view..that means when we switch between portrait to landscape or vice versa the activity is recalled. so need to save the application state in portrait and restore in landscape. so any solution of it?
thanks in advance
Aamirkhan I.
The documentation does a decent job of explaining different ways to handle configuration changes, including screen orientation changes. One of those methods, which is good for saving temporary state of the UI, is saving data in the onSavedInstanceState() method--as #Jason Kuang mentioned.
Generally, you can rely on Android to save and restore the state of Views without any special effort on your part. The source code for the protected method onSaveInstanceState() explains (emphasis added):
The default implementation takes care of most of the UI per-instance state for you by calling android.view.View.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(android.os.Bundle)). 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, otherwise be prepared to save all of the state of each view yourself.
This is a little deceptive, because the API documentation states that EditTexts and TextViews must have android:freezesText="true" explicitly declared on them in your layout XML files to ensure that Android automatically stores their state when onSaveInstanceState() is invoked. I have not tested this recently, but it is what the source code seems to be doing. Therefore, handling temporary UI state on your own is best.
Another tip: You can explicitly prevent the storage of temporary data for a View by calling setSaveEnabled(false) on that View. (This will not affect its children.)
As a rule, it's a good idea to manually save the on-screen state in your onPause() method, and also in onSaveInstanceState().

Override Orientation Change But NOT Restart The Activity AND Pass State Data

I want to be able to change the layout when a device is re-orientated to landscape or portrait. For speed and resource purposes (plus other issues applicable to my app) I do NOT want my app to be destroyed and restarted. I have several objects which I wish to retain between orientation changes as there is no benefit from destroying and re-creating them! I simply just want to change the position of some buttons and TextViews so that they suit the current orientation. Easy right?
Well no it isn't. To achieve the above I included in the app Manifest the configChange option for orientation change. Then I've implemented the onConfigurationChanged() where I determine and apply the appropriate layout. Simple yes?
But now take the textview I have in my layout. How on earth, using this particular method of responding to orientation changes, do I put the same text in the previous textview to the new textview? No instance data is passed to onConfigurationChanged() method. Also for some of the buttons, they could be disabled or enabled... I need to know this after orienatation change.
If I let Android destroy and restart my activity it's first going to create unnecessary work. All I want is to just move a few buttons and textviews.. NOT restart the whole app. That's just ludicrous!
Can anyone help me achieve what need?
An easy way to maintain configuration-independent data is to make use of onRetainNonConfigurationInstance() and its companion method getLastNonConfigurationInstance(). Just return an object that contains all the data that you want to reuse when your activity is recreated.
In Honeycomb, or if you are using the Android compatibility package, you can just call Fragment.setRetainInstance(true) instead. See the docs.

When is it NOT okay to use onRetainNonConfigurationInstance()

Documentation of onRetainNonConfigurationInstance() says "This function is called purely as an optimization, and you must not rely on it being called."
Under what circumstances will onRetainNonConfigurationInstance() not be called?
Some background on what I am trying to achieve: I want to my activity to be notified of orientation change reliably (but also want activity to be killed & restarted as usual).
if you want the activity to restart (calling onCreate->onResume) again on orientation change you don't place configChanges="orientation" in the manifest. you can check the orientation of the screen in the onCreate method of your activity which is probably what you need. if you dont want the activity to restart itself but rather just switch orientation then you add the configChanges flag in the manifest for the activity and then only onConfigurationChanged() will be called where you also can get the screen orientation. the second way is good when you have expensive operations running in the onCreate methods (starting threads quering databases etc) and you want to reuse that info/data for portrait and for landscape. even if you want you can change the layout in onConfigurationChanged() the same way its done in on create but you again need to find the references to all the views because their ids are not the same in that case.
In any case if you want to have a reference to something that existed before the orentation change the configChanges way is better for handling the changes but it requires a bit more work if you are changing layouts or something.

Categories

Resources