As a beginner android developer I have faced with the problem concerning the fragments and activities handling when screen orientation is changed.
This is my situation:
I have two fragments (F_A and F_B). F_A contains ListView (LV), and F_B contains combination of several Views: Description of LV (lets call it as Desc).
When app runs in portrait mode first activity shows LV and after clicking on an item the second activity runs. In this case when the orientation of the screen is changed from portrait to landscape mode I want to change the layout to show both fragments as it is done when the app initially runs in landscape mode.
I hope the provided pieces of codes will not confuse my description of the situation more :)
first activity
main_activity.java file contains:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // there is two actitivy_main.xml files
// one in layout/ directory and the other one in layout-land/ directory.
...
}
#Override
public void respond (int index) {
// this method got the second fragment if it is visible and not null.
// otherwise starts new activity for only description fragment (F_B)
}
second activity (for portrait mode only)
DescriptionActivity.java file contains:
#Override
protected void onSaveInstanceState(Bundle outState) {
// saving some stuff for further handling when orientation will changed from port to land.
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_description); // This layout contains only F_B fragment
... //here some initialization is done for the description.
}
Considering that during the screen rotation the Life Cycle guard methods such as onPause(), onStart() etc. are called I think that I have to check the orientation in that methods then destroy the second activity and run the first: main_activity which will itself check what layout should be shown (in this case the layout from layout-land/ directory, I am not sure weather this is a good idea, so I think to ask for help to developers with more experience.
Many thanks in advance,
Arsen
The best way to achieve what you want is described in Fragments Android documentation, there you have an example with the full explanation.
But just as an advice, you don't have to use 2 diff activities nor check the orientation of the device, you just have to create an activity_main.xml for diff configurations in layout-port (portrait layout) and layout-land (landscape layout) you can learn how in here: Supporting Different Screens and Orientations. I know it's kind of hard at the beginning but working with fragments is one of the best practices for android. Read those tutorials and you'll be able to achieve your goal. Good luck :)
Related
I have 3 Layouts in different folders :
1.layout/HomeLayout.axml (where layout is an folder for layouts and HomeLayout.axml is an global axml file).
2.layout-large-port/HomeLayout.amxl (where layout-large-port is an folder for large(dimension of display) portrait and HomeLayout.axml is axml file).
3.layout-large-land/HomeLayout.axml(where is layout-large-land is an folder for large(dimension of display) landscape and HomeLayout.axml is axml file).
On this activity,i got ListView with some data and when i rotate my phone from portrait to landscape , i need to save all what i maked on Portrait mode(eachrow on listview have different things like buttons/textview and etc) and show on Landscape.
So what is done at current moment. On HomeActivity i declared this :
ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation
This snippet works well,but with big problem : after rotation(from Portrait to Landscape) remains layout Portrait. How can i fix that?
Also i tried to override method OnConfigurationChanged :
public override void OnConfigurationChanged(Android.Content.Res.Configuration newConfig)
{
base.OnConfigurationChanged (newConfig);
if(newConfig.Orientation == Android.Content.Res.Orientation.Landscape)
{
SetContentView(Resource.Layout.ProductsLayout); //system understand that now is Landscape and put correct layout;
}
else
{
SetContentView(Resource.Layout.ProductsLayout); //same here
}
So this method doesnt help me,because when i'm rotating phone,layout sets correct but with no data(listview is empty) and after that,when i'm trying to go back from Landscape to Portrait,layout sets correct but with empty list.
Any suggestions?
PS Sorry for my eng!
Android will completely destroy and recreate your activity when a configuration changes; all views in that activity and the data they hold will also be discarded.
From the Android docs:
Caution: Your activity will be destroyed and recreated each time the
user rotates the screen. 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).
The standard mechanism for persisting data between configuration changes is to save the view data using the OnSaveInstanceState callback of your Activity and then restore the data using the bundle provided through the OnCreate callback.
This is a rough example of how to do so:
public class MainActivity : Activity
{
public const string ARG_LIST_STATE = "list_state";
ListView _listView;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SetContentView (Resource.Layout.Main);
if (bundle != null) {
// Existing data to restore!
if (bundle.ContainsKey (ARG_LIST_STATE)) {
var listState = bundle.GetStringArray (ARG_LIST_STATE);
// TODO: Restore state into list view.
}
}
}
protected override void OnSaveInstanceState (Bundle outState)
{
string[] listState= new string[5]; // TODO: Grab data from the list view and serialize it into the outState
outState.PutStringArray(ARG_LIST_STATE, listState);
base.OnSaveInstanceState (outState);
}
}
Another example is provided by Xamarin here.
I strongly recommend that you review the activity lifecycle docs so you can better understand how activities are managed by Android.
Solution was(For me) to make acceptable design for portrait,which is it does not differ landscape mode(just use mechanisms like sum_weight and etc).
I do not know if the title is correct, here is what happens.
I have an application which works differently on a phone and on a tablet, on a phone it shows as portrait on a tablet it shows as landscape.
To achieve this I created a class called CoreActivity which is extended by all my activities and does the following:
public class CoreActivity extends Activity {
protected boolean _landscape = false;
public boolean isPhone() {
int layoutSize = getScreenLayoutSize();
return (layoutSize == Configuration.SCREENLAYOUT_SIZE_SMALL || layoutSize == Configuration.SCREENLAYOUT_SIZE_NORMAL);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isPhone() && !_landscape) {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}
protected int getScreenLayoutSize() {
return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK);
}
}
My problem occurs when I wish to show a screen on a phone setup on landscape mode, to do this I use the following:
#Override
protected void onCreate(Bundle savedInstanceState) {
_landscape = true
super.onCreate(savedInstanceState);
}
The problem is, that on a phone, if the user is holding the phone on portrait mode (as one would, since most of the application is in portrait mode) then the activity gets created and destroyed and then recreated. But if they are holding it on landscape mode, then it is only created once.
My problem occurs because on the onCreate method I launch the threads that load data, and I also show fragments.
Is there a way to avoid this problem? is there a way to launch an activity from the start on portrait mode and not change it, or have it not create twice?
Thanks in advance for any help you can provide
Recreating occurs just because you are forcing to, by calling setRequestedOrientation probably in order to set screen orientation. However you don't need to check and change it by code. You can do it via xml file. You can set different xml files depending on the screen size. But it is little bit hack so there is no guarantee in the future.
On the manifest file you can force by:
<activity
android:name="com.my.example.MyActivity"
android:screenOrientation="landscape"/> // or portrait
So as far as I understand you want to force portrait for small sizes and landscape(or sensor) for larger screen sizes. But above configuration applies for all screen sizes. Here is the tricky part: landscape portrait sensor etc. are all integers defined here.
As you might guess you can write android:screenOrientation=0 instead of landscape. What we know from beginning lessons of android, we can define integers in xml files then their values might vary on screen size. So..
You should first create different integers.xml files for different screen sizes. i.e.:
values
- integers.xml
values-large
- integers.xml
for values/integers.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="orientation">1</integer> // 1 for portrait
</resources>
for values-large/integers.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="orientation">0</integer> // 0 for landscape
</resources>
and finally you you have to manipulate manifest file by:
<activity
android:name="com.my.example.MyActivity"
android:screenOrientation="#integer/orientation"/> // read constant from xml files
You can also use sensor , fullSensor, nosensor, locked, behind, reverseLandscape, reversePortait options too. But I am warning you, this is a hack solution.
Well, I found a solution to this issue, just declare:
android:screenOrientation="locked"
on every activity having this issue in the manifest.
And keep using setRequestedOrientation() programatically to define if landscape or portrait orientation within onCreate() method,
It will work! ;)
If all your activites must have the same orientation on a device, you can start your application with a splash screen activity, set the orientation like in your current code and forward to your CoreActivity or any other activity in your App.
In your AndroidManifest.xml set
<activity
android:name=".CoreActivity"
android:screenOrientation="behind"/>
This will use the same orientation as the activity that's immediately beneath it in the activity stack.
Did you try adding android:configChanges="keyboard|keyboardHidden|orientation" to the <activity>-Tag inside of your AndroidManifest.xml?
This should prevent the system from restarting the Activity, but I am not sure whether this will actually work when forcing the orientation programatically. It's worth a shot though.
try this in your manifest.xml...
this will stop the recalling the onCreate() multiple time while orientation changes...
android:configChanges="keyboardHidden|orientation|screenSize"
You dont need to handle this manually. Android has built it support for different screen sizes
check this link
http://developer.android.com/training/multiscreen/screensizes.html
if the question sounds weird at first, here comes the explanation:
I have got an activity that hosts my three fragments. Since I would like one of my fragments to save its instance state when the device is rotated, I defined this in my manifest for my activity that hosts the fragments:
android:configChanges="orientation|screenSize"
This works just fine. However, now I have got an other problem: One of my other fragments uses a special landscape layout. The problem is, that this layout is not used immediately on device rotation. I think it is because the new layout only gets set on onCreate.
What can I do to solve this problem? I want my landscape layout to be set immediately.
You can put
setRetainInstance(true);
in onCreateView(); method of your Fragment. I think it should do the trick.
As far as I know you down need to add the configChanges parameter to your manifest.
You can override onSaveInstanceState() in your Fragment
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putInt(KEY_INDEX, someIntValue);
}
This methode should be called before your fragment gets destroyed.
Now in your onCreate(Bundle savedInstanceState) (or onCreateView()) methode:
if (savedInstanceState != null) {
someIntValue = savedInstanceState.getInt(KEY_INDEX);
}
This way it shouldn't intervene with any other special fragments.
My app have two different layout for portrait and landscape mode which both encapsulate a webview and some buttons. Buttons are at bottom in portrait and at left in landscape so more reading space is available in webview.
The problem is, the activity is recreated on screen rotation and webview loads the first page which is not a wanted behavior.
I searched and found out using android:configChanges="orientation" in activity tag prevents recreating of the activity. But the porblem is it prevents the layout changing too as it happens in activity creation.
I want my program to work in 2.2, waht's the best way to this?
I tested fragments, but dealing with fragment makes things much more complex and the fragment itself needs saving and restoring which may not work in a webview which has javascript state, So I searched more and find a nice article somewhere and with some modification I came to a solution which I suggest:
First, add android:configChanges="orientation|screenSize|keyboard|keyboardHidden" to manifest so app handles the config change instead of android.
Make two different layout for lnadscape and portrait mode and put them in corresponding layout folders. In both layouts instead of webview place a LinerLayout which acts as a placeholder for webview.
In code define initUI method like this and put every thing related to UI initialization in this method:
public void initui()
{
setContentView(R.layout.main);
if (wv == null) wv = new WebView(this);
((LinearLayout)findViewById(R.id.webviewplace)).addView(wv);
findViewById(R.id.home).setOnClickListener(this);
}
If the webview doesn't exist, it will be created and after setContentView(R.layout.main) it will be added to the layout. Other UI customization came afterward.
and in onConfigurationChanged:
#Override
public void onConfigurationChanged(Configuration newConfig)
{
((LinearLayout)findViewById(R.id.webviewplace)).removeAllViews();
super.onConfigurationChanged(newConfig);
initUI();
}
In onConfigChange First the webview is removed from old place holder and initui will be called which will add it back to the new layout.
and in oncreate call initui so the ui will be initialized for the first time.
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
initUI()
}
Could you not save the last loaded URL in the webview in onSaveInstanceState(Bundle outState) and then make sure to reload it onRestoreInstanceState(Bundle savedInstanceState) using myWebview.loadUrl(restoredUrl)?
edit I know that this might not work if the web page you are displaying requires a state to be kept. But if not it should be a solution to your problem.
From this document https://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange
I just edited manifest with android:configchanges so it works fine for me.
<activity android:name=".Webhtml"
android:configChanges="orientation|screenSize"/>
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);
}