ost modern, self-respecting mobile operating systems in today's market support different screen orientations based on the position of the device. Android is no exception. While this feature is often taken for granted, it's something on which developers spend extra time, ensuring that their applications work flawlessly regardless of screen orientation. This article will show you how screen orientation works in Android and some techniques to make your life easier.
To test the concepts discussed in this article, connect the G1 to your computer. In Eclipse, press F11 to deploy the application that you have just created to the G1. Figure 1 shows the UI of the application when the keyboard is closed. When the keyboard is opened, the orientation changes automatically to landscape mode, as shown in Figure 2.
![]() Figure 1. Portrait: Here's the device display in portrait mode. | ![]() Figure 2. Landscape: Here's the device display in landscape mode. |
![]() | |
| Figure 3. Error: This is an activity that does not know how to react to a change in orientation. |
Reacting to changes in screen orientation is important because it affects the look of your UI. The left side of Figure 3 shows an application designed to display in portrait mode. However, if special care is not taken to adapt the UI for landscape mode, it will look like the right side of Figure 3 when the device changes to landscape mode.
There are three general techniques you can employ to handle changes in screen orientation:
Anchoring and Centralizing
Anchoring and centralizing can be easily achieved by using RelativeLayout. Consider main.xml (see Listing 1), which contains five Button views embedded within the
Observe the following attributes found in the various Button views:
![]() Figure 4. Portrait Mode: The image shows the activity in portrait mode. | ![]() Figure 5. Landscape Mode:. This is what it looks like when all views are aligned appropriately. |
When the screen orientation changes to landscape mode, the four buttons are aligned to the four edges of the screen and the center button is centered in the middle of the screen with its width fully stretched (see Figure 5).
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<Button
android:id="@+id/button1"
android:layout_width="121px"
android:layout_height="44px"
android:text="Button"
android:layout_x="13px"
android:layout_y="13px"
>
</Button>
</AbsoluteLayout>
Here, the activity has only one button view as shown in Figure 6.
![]() Figure 6. Solo: This shows the activity with one view. | ![]() Figure 7. Redone: The Button view is repositioned and resized when it is in landscape mode. |
When the screen orientation changes to landscape mode, you might want to reposition the button view. In this case, you need to write some code to determine the current screen orientation the activity is in and then manually set the layout parameter of the view to reposition.
To see how this is done, use the code in Listing 2 in Orientation.java.
The above program first checks the current screen size and determines the orientation. It then creates a new LayoutParams object (containing the new position to locate the view) to be used by the Button view.
Figure 7 shows the application in actionthe Button view is repositioned and resized when the screen orientation changes to landscape mode.
![]() Figure 8. Downloading Data: The layout-land folder contains the UI to be displayed in landscape mode. | ![]() Figure 9. Emulate 3G: You can also apply the –land name extension to the drawable folder. |
Basically, the main.xml file contained within the layout folder defines the UI for the activity in portrait mode while the main.xml file in the layout-land folder defines the UI in landscape mode. The following shows the content of main.xml under the layout-land folder:
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<Button
android:id="@+id/button1"
android:layout_width="150px"
android:layout_height="60px"
android:text="Button"
android:layout_x="280px"
android:layout_y="180px"
/>
</AbsoluteLayout>
Basically, the main.xml file contained within the layout folder defines the UI for the activity in portrait mode while the main.xml file in the layout-land folder defines the UI in landscape mode. The following shows the content of main.xml under the layout-land folder:
<?xml version="1.0" encoding="utf-8"?&glt;
<AbsoluteLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
&glt;
<Button
android:id="@+id/button1"
android:layout_width="150px"
android:layout_height="60px"
android:text="Button"
android:layout_x="280px"
android:layout_y="180px"
/&glt;
</AbsoluteLayout&glt;
Using this method, when the orientation of the device changes, Android will automatically load the appropriate XML file.
Besides using the –land name extension on the res/layout folder, you can also use it on the res/drawable folder. For example, the res/drawable-land folder shown in Figure 9 contains images that are designed to be displayed in landscape mode while those in the res/drawable folder are designed to be displayed in portrait mode.
First and foremost, you should name all your views in your activity using the android:id attribute. This attribute is important for applications that change with screen orientation, because when Android destroys activities, it saves state only for named views. For example, suppose a user changes orientation in the midst of entering some text into an EditText view. When this happens, if you've named the EditText view using an android:id attribute, Android will persist any existing text inside the EditText view, and restore it automatically when the activity is recreated. In contrast, if the view does not have an android:id attribute, the system will not be able to persist the text, so when it recreates the activity, any already-entered text will be lost.
In addition, note that the onSaveInstanceState event is fired whenever an activity is about to be killed or put into the background. For example, when the orientation is changed, this event is fired so that the current state of the activity can be saved and restored later. This event is similar to the onPause event of the Activity class, but unlike the onPause event (which is called whenever the activity is being placed in the background or being killed), it is not fired when an activity is being unloaded from the stack (since there is no need to restore its state later on).
You can override the onSaveInstanceState event and save the information you need in this event. For example, the following code shows that you can save the string ID into the Bundle object during the onSaveInstanceState event:
@Override
public void onSaveInstanceState(Bundle outState)
{
//---save whatever you need to persist—
outState.putString("ID", "1234567890");
super.onSaveInstanceState(outState);
}
When an activity is recreated, the onCreate event is fired first, followed by the onRestoreInstanceState event. This onRestoreInstanceState event allows you to retrieve the state that you have saved previously in the onSaveInstanceState event:
While you can use the onSaveInstanceState event to save state information, the limitation is that you can only save your state information into a Bundle object. If you need to save more complex data structures, then this is not an adequate solution.@Override public void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); //---retrieve the information persisted earlier--- String ID = savedInstanceState.getString("ID"); }
Another event handler that you can use is the onRetainNonConfigurationInstance event. This event is fired when an activity is about to be destroyed due to a configuration change. (Screen orientation changes are considered configuration changes, and by default, all configuration changes cause the current activity to be destroyed). You can save your current data structure by returning it in this event, like this:
@Override
public Object onRetainNonConfigurationInstance()
{
return("Hello");
}
Note that this event returns an Object type, which pretty much allows you to return any data type. To extract the saved data, you can extract it in the onCreate event, using the getLastNonConfigurationInstance() method, like this:
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String str = (String) getLastNonConfigurationInstance();
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.learn2develop.UIExample"
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".UIActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter&glt;
</activity>
</application>
</manifest>
The above attribute indicates that you want to handle the flipping of the keyboard and the changes in the accelerometer (sensor) yourself. When a change in orientation occurs, the onConfigurationChanged event will be fired, which you can override to redraw your activity:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
//---code to redraw your activity here---
//...
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.leanr2develop.OrientationAware"
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".Orientation"
android:screenOrientation="sensor"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The above specifies that the screen orientation for the Orientation activity is based on the sensor (accelerometer) of the device. If you hold the device upright, the screen will be displayed in portrait mode; if you hold it sideways, it will change to landscape mode.
package net.learn2develop.OrientationAware;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
public class Orientation extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//---change to landscape mode---
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}
To change to portrait mode, use the ActivityInfo.SCREEN_ORIENTATION_PORTRAIT constant:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
Besides using the setRequestOrientation() method, you can also use the android:screenOrientation attribute on the <activity> element in AndroidManifest.xml as follows to fix the activity to a certain orientation:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.learn2develop.UIExample"
android:versionCode="1"
android:versionName="1.0.0">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name=".UIActivity"
android:screenOrientation="landscape"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
The above example fixes the activity to a certain orientation (landscape in this case) and prevents the activity from being destroyed; that is, the activity will not be destroyed and the onCreate event will not be fired again when the orientation changes.
Now, you've seen the various ways to handle screen orientation changes in Android and how an activity gets recreated when an activity changes orientation. You should be able to use these methods to persist your state during transition.
| DevX is a division of Internet.com. © Copyright 2010 Internet.com. All Rights Reserved. Legal Notices |