Rotation and the Activity Lifecycle
Let’s get back to the bug you found at the end of Chapter 2. Run GeoQuiz, press the Next button to reveal the second question, and then rotate the device. (On the emulator, press Fn+Control+F12/Ctrl+F12 to rotate.)
After rotating, GeoQuiz will display the first question again. Check LogCat to see what has happened. Your output should look like Figure 3.8.
Figure 3.8 QuizActivity is dead. Long live QuizActivity!
When you rotated the device, the instance of QuizActivity that you were looking at was destroyed, and a new one was created. Rotate the device again to witness another round of destruction and rebirth.
This is the source of your bug. Each time a new QuizActivity is created, mCurrentIndex is initialized to 0, and the user starts over at the first question. You will fix this bug in a moment. First, let’s take a closer look at why this happens.
Device configurations and alternative resources
Rotating the device changes the device configuration. The device configuration is a set of characteristics that describe the current state of an individual device. The characteristics that make up the configuration include screen orientation, screen density, screen size, keyboard type, dock mode, language, and more.
Typically, applications provide alternative resources to match different device configurations. You saw an example of this when you added multiple arrow icons to your project for different screen densities.
Screen density is a fixed component of the device configuration; it cannot change at runtime. On the other hand, some components, like screen orientation, can change at runtime.
When a runtime configuration change occurs, there may be resources that are a better match for the new configuration. To see this in action, let’s create an alternative resource for Android to find and use when the device’s screen orientation changes to landscape.
Creating a landscape layout
In the Project tool window, right-click the res directory and select New → Android resource directory. You should see a window similar to Figure 3.9 that lists the resource types and qualifiers for those types. Select layout in the Resource type drop-down box. Leave the Source set option set to main. Next, you will choose how the layout resources will be qualified. Select Orientation in the Available qualifiers list and click the >> button to move Orientation to the Chosen qualifiers section.
Figure 3.9 Creating a new resource directory
Finally, ensure that Landscape is selected in the Screen Orientation dropdown, as shown in Figure 3.10. Verify that the Directory name now indicates that your directory is called layout-land. While this window looks fancy, its purpose is just to set the name of your directory. Click OK and Android Studio will create the res/layout-land/ folder.
Figure 3.10 Creating res/layout-land
The -land suffix is another example of a configuration qualifier. Configuration qualifiers on res subdirectories are how Android identifies which resources best match the current device configuration. You can find the list of configuration qualifiers that Android recognizes and the pieces of the device configuration that they refer to at http://developer.android.com/guide/topics/resources/providing-resources.html.
When the device is in landscape orientation, Android will find and use resources in the res/layout-land directory. Otherwise, it will stick with the default in res/layout/. However, at the moment there are no resources in the res/layout-land directory. Let’s fix that.
Copy the activity_quiz.xml file from res/layout/ to res/layout-land/. You now have a landscape layout and a default layout. Keep the filename the same. The two layout files must have the same filename so that they can be referenced with the same resource ID.
Now make some changes to the landscape layout so that it is different from the default. Figure 3.11 shows the changes that you are going to make.
Figure 3.11 An alternative landscape layout
The FrameLayout will replace the LinearLayout. FrameLayout is the simplest ViewGroup and does not arrange its children in any particular manner. In this layout, child views will be arranged according to their android:layout_gravity attributes.
The TextView, LinearLayout, and Button children of the FrameLayout need android:layout_gravity attributes. The Button children of the LinearLayout will stay exactly the same.
Open layout-land/activity_quiz.xml and make the necessary changes using Figure 3.11. You can use Listing 3.4 to check your work.
Listing 3.4 Tweaking the landscape layout (layout-land/activity_quiz.xml)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" ><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/question_text_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:padding="24dp" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|center_horizontal" android:orientation="horizontal" > ... </LinearLayout> <Button android:id="@+id/next_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|right" android:text="@string/next_button" android:drawableRight="@drawable/arrow_right" android:drawablePadding="4dp" /> </LinearLayout></FrameLayout>
Run GeoQuiz again. Rotate the device to landscape to see the new layout (Figure 3.12). Of course, this is not just a new layout – it is a new QuizActivity as well.
Figure 3.12 QuizActivity in landscape orientation
Rotate back to portrait to see the default layout and yet another new QuizActivity.
Android does the work of determining the best resource for you, but it has to create a new activity from scratch to do it. For a QuizActivity to display a different layout, setContentView(R.layout.activity_quiz) must be called again. And this will not happen unless QuizActivity.onCreate(...) is called again. Thus, Android destroys the current QuizActivity on rotation and starts fresh to ensure that it has the resources that best match the new configuration.
Note that Android destroys the current activity and creates a new one whenever any runtime configuration change occurs. A change in keyboard availability or language could also occur at runtime, but a change in screen orientation is the runtime change that occurs most frequently.