I have a monitoring app that displays a set of four parameters as animations (for example, it displays the temperature using a thermometer graphic). I also have one screen which plots all the four parameter values on a chart.
My requirement is this:
Whenever the device is in Landscape mode, the chart should be displayed.
Whenever the device is back in portrait mode, the earlier animation screen should be displayed.
I'm wondering if it is possible to provide different functionality based on the orientation. I see 2 options
Override Activity.onConfigurationChanged() - but then what? Can i do a setContentView() at this point? Or, can i launch a different Activity (one which displays the Chart in a separate, lanscape-mode-only Activity)?
Override Application.onConfigurationChanged() method. But I frankly haven't a clue as to how to proceed with this.
Yes you can call setContentView() inside Activity.onConfigurationChanged(). You can check orientation and accordingly perform some tasks, i.e. run different code based on orientation.
Normal behaviour is that when orientation changes, system kills Activity and creates a new one. You can prevent this with android:configChanges="orientation" in you activity manifest. In this case you need to override the onConfigurationChanged(..) method, which will be called when change happens. See handling runtime changes.
Related
I have two screens and two xml-files for one activity. In my onCreate method I call the fist one and handle some user input. After that I am changing my layout with
setContentView(R.layout.activity_quiz2);
and everything is fine and works as intended but when I run the application and turn my device after I switched the layouts. It will just switch back to the first layout and stay on that layout (every button and all works as well).
How can I prevent my screen from changing back my layouts when I turn my device?
It's switching back to the first layout because when you rotate the screen the whole Activity is recreated. You have couple of options to deal with this:
Add android:configChanges="orientation" to your <activity element in the AndroidManifest.xml file. This way you're telling the system you want to deal with the rotation yourself (and if needed you can perform some actions in onConfigurationChanged() method).
Use Fragments to display the two "states" you have (I'm assuming those are quiz1 and quiz2). This way you won't need to do multiple setContentView() in your activity.
The second option is the one to go for, as that's basically what Fragments are all about. Not to mention you can do very nice transitions between them (fade-in-out animations, etc) and your UX will be much better than when swapping the setContentView().
Whenever I use setRequestedOrientation(int) it behaves differently from when I declare the orientation in my Manifest.xml.
(this is expected)
I read that (from docs):
If the activity is currently in the foreground or otherwise impacting the screen orientation, the screen will immediately be changed (possibly causing the activity to be restarted)
Practically this means that whenever an app is started, while the device has an orientation that is NOT the orientation that will be set with setRequestedOrientation(int), the activity will be created twice (once 'normally' and once to reflect requested orientation).
As of now it is not worthwhile to make my activites 100% config-change-proof, what I would like is a way to set the requested orientation, in code, without having to create my activities twice. Is there a way?
(Preferably without using hackish workaround such as setting the 'orientation' flag in configChanges)
EDIT:
Accepted answer is right, it simply isn't logical (doesn't make sense). For new readers, what I did was add a stub-activity (mainly empty activity that doesn't do much). All it does is use getRequestedOrientation() when it differs from what I want it to be, I will call setRequestedOrientation(int), otherwise start the activity I want and in its onCreate also call the setRequestedOrientation(int) to keep it in requested orientation.
No, that doesn't make sense. If you set the orientation programmatically, the Activity is already running before you can do anything to change the duplicate creation for each orientation.
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.
I was wondering why not use android:configChanges="keyboardHidden|orientation" in every (almost every ;)) activity?
Goods:
no need to worry about your activity been rotated
it's faster
Not so nice:
need to change your layouts if they are depending on screen size (e.g. layouts with two columns or so)
Bad:
no flexible way to have different layouts on different orientation
not so good when using fragments
But if we don't use different layouts, why not?
Quick Background
By default, when certain key configuration changes happen on Android (a common example is an orientation change), Android fully restarts the running Activity to help it adjust to such changes.
When you define android:configChanges="keyboardHidden|orientation" in your AndroidManifest, you are telling Android: "Please don't do the default reset when the keyboard is pulled out, or the phone is rotated; I want to handle this myself. Yes, I know what I'm doing"
Is this a good thing? We shall soon see...
No worries?
One of the pros you start with is that there is:
no need to worry about your activity been rotated
In many cases, people mistakenly believe that when they have an error that is being generated by an orientation change ("rotation"), they can simply fix it by putting in android:configChanges="keyboardHidden|orientation".
However, android:configChanges="keyboardHidden|orientation" is nothing more than a bandaid. In truth, there are many ways a configuration change can be triggered. For example, if the user selects a new language (i.e. the locale has changed), your activity will be restarted in the same way it does by an orientation change. If you want you can view a list of all the different types of config changes.
Edit: More importantly, though, as hackbod points out in the comments, your activity will also be restarted when your app is in the background and Android decides to free up some memory by killing it. When the user comes back to your app, Android will attempt to restart the activity in the same way it does if there was some other configuration change. If you can't handle that - the user will not be happy...
In other words, using android:configChanges="keyboardHidden|orientation" is not a solution for your "worries." The right way is to code your activities so that they are happy with any restart Android throws at them. This is a good practice that will help you down the road, so get used to it.
So when should I use it?
As you mentioned there is a distinct advantage. Overwriting the default configuration change for a rotation by handling it yourself will speed things up. However, this speed does come with a price of convenience.
To put it simply, if you use the same layout for both portrait and landscape you're in good shape by doing the overwrite. Instead of a full-blown reload of the activity, the views will simply shift around to fill the remaining space.
However, if for some reason you use a different layout when the device is in landscape, the fact that Android reloads your Activity is good because it will then load up the correct layout. [If you use the override on such an Activity, and want to do some magical re-layout at runtime... well, good luck - it's far from simple]
Quick Summary
By all means, if android:configChanges="keyboardHidden|orientation" is right for you, then use it. But PLEASE be sure to test what happens when something changes, because an orientation change is not the only way a full Activity restart can be triggered.
From my point of view: If the layout is the same in both landscape and portrait mode - you might aswell disable one of the two in your app.
The reason why I state this is that I as a user expect the app to provide me with some benefit, when I change orientation. If it doesn't matter how I hold my phone, then I don't need the choice.
Take for instance an app where you have a ListView, and upon clicking a ListItem you want to be shown a detailed view for that item. In landscape you would od this by dividing the screen in two, having the ListView on the left and the detailed view on the right. In Portrait you would have the list in one screen and then change the screen to the detailed view when a ListItem is selected. In that case orientation change makes sense as well as different layouts.
I don see why.... occasional restarts are ok in my opinion... configChanges handles most cases for me... well maybe in some types of applications this can be problem but it depends really on type of app and how you restore state when app restarts... When one of my app restarts user is logged back and last activity opens by my code and user jus loses some steps to go back where he was but not big deal.. In other some state is always persisted and some state is always restored on restart. When activity restarted it had to be that app have not been used or something... so no problem at all... In game for example this can be problem maybe or in some other type of app I don't know...
I say that when you do it this way applications just works fine under normal circumstances. And code is much more readable without ton of logic needed for saving and restoring where u just can make new bugs and have to maintain it all the time... sure if android gets out of power and kill you application window it lose the context and starts again, but this happen just in special situations and on newer devices I belive this is more and more rare...
So kill me, but I use this across applications quite successfully...
android:configChanges="locale|keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
But I understand that for some special kind of applications it may be not good way but most of apps can live with this just OK.
Yeah I think pausing will make it quicker than releasing the player. Still have the pause though.
Have now found a solution that won't pause the song.
State in the manifest that you will handle the config change for screen orientation and then use the onConfigurationChanged method to load the layout file. By doing this in logCat I can see onPause, onCreate & onResume aren't called, and therefore the song isn't paused.
update the manifest to handle the orientation.
android:configChanges="orientation|screenSize"
add this code
#Override
public void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
setContentView(R.layout.activity_main);
}
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.