1. Welcome
Introduction
The MaterialMe app that you created in a previous chapter doesn't properly handle device-orientation changes from portrait (vertical) mode to landscape (horizontal) mode. On a tablet, the font sizes are too small, and the space is not used efficiently.
The Android framework has a way to solve both issues. Resource qualifiers allow the Android runtime to use alternate XML resource files depending on the device configuration—the orientation, the locale, and other qualifiers. For a full list of available qualifiers, see Providing alternative resources.
In this practical you optimize the use of space in the MaterialMe app so that the app works well in landscape mode and on tablets. In another practical on using the layout editor, you learned how to create layout variants for horizontal orientation and tablets. In this practical you use an adaptive layout, which is a layout that works well for different screen sizes and orientations, different devices, different locales and languages, and different versions of Android.
What you should already know
You should be able to:
- Create and run apps in Android Studio.
- Create and edit UI elements using the layout editor.
- Edit XML layout code, and access elements from your Java code.
- Create a click handler for a
Button
click. - Use drawables, styles, and themes.
- Extract text to a string resource and a dimension to a dimension resource.
What you'll learn
- How to create alternate resources for devices in landscape mode.
- How to create alternate resources for tablets.
- How to create alternate resources for different locales.
What you'll do
- Update the MaterialMe app for better use of space in landscape mode.
- Add an alternative layout for tablets.
- Localize the content of your app.
2. App overview
The updated MaterialMe app will include an improved layout for landscape mode on phones. It will also include improved layouts for portrait and landscape modes on tablets, and it will offer localized content for users outside the United States.
The screenshot below shows a phone running the updated MaterialMe app in landscape orientation:
The screenshot below shows a tablet running the updated MaterialMe app, with qualifiers to show two columns when running in portrait orientation:
3. Task 1: Support landscape orientation
You may recall that when the user changes the orientation of the device, the Android framework destroys and recreates the current activity. The new orientation often has different layout requirements than the original one. For example, the MaterialMe app looks good in portrait mode, but does not make optimal use of the screen in landscape mode. With the larger width in landscape mode, the image in each list item overwhelms the text providing a poor user experience.
In this task, you create an alternative resource file that will change the appearance of the app when it is used in landscape orientation.
1.1 Change to a GridLayoutManager
Layouts that contain list items often look unbalanced in landscape mode when the list items include full-width images. One good solution is to use a grid instead of a linear list when displaying CardView
elements in landscape mode.
Recall that the items in a RecyclerView
list are placed using a LayoutManager
; until now, you have been using the LinearLayoutManager
which lays out each item in a vertical or horizontal scrolling list. GridLayoutManager
is another layout manager that displays items in a grid, rather than a list.
When you create a GridLayoutManager
, you supply two parameters: the app context
, and an integer representing the number of columns. You can change the number of columns programmatically, which gives you flexibility in designing adaptive layouts. In this case, the number of columns integer should be 1 in portrait orientation (single column) and 2 when in landscape mode. Notice that when the number of columns is 1, a GridLayoutManager
behaves similar to a LinearLayoutManager
.
This practical builds on the MaterialMe app from the previous practical.
- Continue developing your version of the MaterialMe app, or download MaterialMe. If you decide to make a copy of the MaterialMe project to preserve the version from the previous practical, rename the copied version MaterialMe-Resource.
- Create a new resources file called
integers.xml
. To do this, open the res folder in the Project > Android pane, right-click (or Control-click) on the values folder, and select New > Values resource file. - Name the file integers.xml and click OK.
- Create an integer constant between the
<resources>
tags calledgrid_column_count
and set it equal to 1:
<integer name="grid_column_count">1</integer>
- Create another values resource file, again called integers.xml; however, the name will be modified as you add resource qualifiers from the Available qualifiers pane. The resource qualifiers are used to label resource configurations for various situations.
- Select Orientation in the Available qualifiers pane, and press the >> symbol in the middle of the dialog to assign this qualifier.
- Change the Screen orientation menu to Landscape, and notice how the directory name
values-land
appears. This is the essence of resource qualifiers: the directory name tells Android when to use that specific layout file. In this case, that is when the phone is rotated to landscape mode. - Click OK to generate the new layout file.
- Copy the integer constant you created into this new resource file, but change the value to 2.
You should now have two individual integers.xml
files grouped into an integers.xml
folder in the Project > Android pane. The second file is labeled with the qualifier you selected, which is land
in this case. The qualifier appears in parentheses: integers.xml (land)
.
1.2 Modify MainActivity
- Open MainActivity, and add code to
onCreate()
to get the integer from theintegers.xml
resource file:
int gridColumnCount =
getResources().getInteger(R.integer.grid_column_count);
The Android runtime will take care of deciding which integers.xml
file to use, depending on the state of the device.
- Change the
LinearLayoutManager
for theRecyclerView
to aGridLayoutManager
, passing in the context and the newly created integer:
mRecyclerView.setLayoutManager(new
GridLayoutManager(this, gridColumnCount));
- Run the app and rotate the device. The number of columns changes automatically with the orientation of the device.
When using the app in landscape mode, you will notice that the swipe to dismiss functionality is no longer intuitive, since the items are now in a grid rather than a single column. In the next steps, you turn off the swipe action if there is more than one column.
- Use the
gridColumnCount
variable to disable the swipe action (setswipeDirs
to zero) when there is more than one column:
int swipeDirs;
if(gridColumnCount > 1){
swipeDirs = 0;
} else {
swipeDirs = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
- Use
swipeDirs
in place of the swipe direction arguments (ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT
) forItemTouchHelper.SimpleCallback()
:
ItemTouchHelper helper = new ItemTouchHelper(new
ItemTouchHelper.SimpleCallback(ItemTouchHelper.LEFT |
ItemTouchHelper.RIGHT |
ItemTouchHelper.DOWN | ItemTouchHelper.UP,
swipeDirs) {
- Run the app and rotate the device. In landscape (horizontal) orientation, the user can no longer swipe to delete a card.
4. Task 2 : Support tablets
Although you have modified the app to look better in landscape mode, running it on a tablet with physically larger dimensions results in all the text appearing too small. Also when the device is in landscape orientation, the screen is not used efficiently; three columns would be more appropriate for a tablet-sized screen in landscape mode.
In this task, you add additional resource qualifiers to change the appearance of the app when used on tablets.
2.1 Adapt the layout to tablets
In this step, you create different resource qualifiers to maximize screen use for tablet-sized devices, increasing the column count to 2 for portrait (vertical) orientation and 3 for landscape (horizontal) orientation.
The resource qualifier you need depends on your specific requirements. When creating a new resource file, there are several qualifiers in the Available qualifiers pane that you can use to select the correct conditions:
- Smallest Screen Width: This qualifier is used most frequently to select for tablets. It is defined by the smallest width of the device (regardless of orientation), which removes the ambiguity when talking about "height" and "width" since some devices are traditionally held in landscape mode, and others in portrait. Anything with a smallest width of at least 600dp is considered a tablet.
- Screen Width: The screen width is the effective width of the device, regardless of the orientation. The width changes when the device is rotated, since the effective height and width of the device are switched.
- Screen Height: Same as Screen Width, except it uses the effective height instead of the effective width.
To start this task:
- Create an integers.xml resource file which uses the Smallest Screen Width qualifier with the value set to 600. Android uses this file whenever the app runs on a tablet.
- Copy the code from the integers.xml (land) file (it has a grid count of 2) and paste it in the new integers.xml (sw600dp) file.
- Create another integers.xml file that includes both the Smallest Screen Width qualifier set to 600, and the Orientation qualifier set to Landscape. Android uses the resulting
integers.xml (sw600dp-land)
file when the app runs on a tablet in landscape mode. - Copy the code from the integers.xml (land) file and paste it in the new integers.xml (sw600dp-land) file.
- Change the
grid_column_count
variable to 3 in the integers.xml (sw600dp-land) file. - Run the app on a tablet or tablet emulator, and rotate it to landscape mode. The app should show three columns of cards, as shown in the first figure below. Rotate it to portrait mode, and the app should show two columns of cards, as shown in the second figure below. With these resource qualifier files, the app uses the screen real estate much more effectively.
2.2 Update the tablet list item styles
At this point, your app changes the number of columns in a GridLayoutManager
to fit the orientation of the device and maximize the use of the screen. However, the TextView
elements that appeared correctly-sized on a phone's screen now appear too small for the larger screen of a tablet.
To fix this, you extract the TextAppearance
styles from the layout resource files into the styles.xml
resource file. You will also use resource qualifiers to create additional styles.xml
files for tablets.
Follow these steps to add the TextAppearance
styles:
- Open styles.xml and add the following styles:
<style name="SportsDetailText"
parent="TextAppearance.AppCompat.Subhead"/>
<style name="SportsTitle"
parent="TextAppearance.AppCompat.Headline"/>
- Create a new
values
resource file called styles.xml that uses the Smallest Screen Width qualifier with a value of 600 for tablets. - Copy all styles from the original styles.xml file into the new styles.xml (sw600dp) file.
- In styles.xml (sw600dp), change the
parent
of theSportsTitle
style to "TextAppearance.AppCompat.Display1":
<style name="SportsTitle"
parent="TextAppearance.AppCompat.Display1"/>
- The Android predefined
Display1
style uses thetextColorSecondary
value from the current theme (ThemeOverlay.AppCompat.Dark
), which in this case is a light gray color. The light gray color does not show up well on the banner images in your app. To correct this add an"android:textColor"
attribute to theSportsTitle
style and set it to "?android:textColorPrimary":
<style name="SportsTitle"
parent="TextAppearance.AppCompat.Display1">
<item name=
"android:textColor">?android:textColorPrimary</item>
</style>
The question mark tells Android runtime to find the value in the theme applied to the View
. In this example the theme is ThemeOverlay.AppCompat.Dark
in which the textColorPrimary
attribute is white.
- Change the
parent
ofSportsDetailText
style to "TextAppearance.AppCompat.Headline". - To update the style of the
TextView
elements, open list_item.xml, and change thestyle
attribute of thetitle
TextView
to @style/SportsTitle:
style="@style/SportsTitle"
- Change the
style
attribute of thenewsTitle
andsubTitle
TextView
elements to@style/SportsDetailText
. - Run your app on a tablet or tablet emulator. Each list item now has a larger text size on the tablet.
2.3 Update the tablet sports detail styles
You have now fixed the display for the MainActivity
, which lists all the Sports
CardView
elements. The DetailActivity
still has the same font sizes on tablets and phones.
- Add the following
style
in the styles.xml file for the detail title:
<style name="SportsDetailTitle"
parent="TextAppearance.AppCompat.Headline"/>
- Add the following
style
in the styles.xml (sw600dp) file for the detail title:
<style name="SportsDetailTitle"
parent="TextAppearance.AppCompat.Display3"/>
- Open activity_detail.xml, and change the
style
attribute of both thenewsTitleDetail
andsubTitleDetail
TextView elements to the newSportsDetailText
style you created in a previous step:
style="@style/SportsDetailText"
- In activity_detail.xml, change the
style
attribute of thetitleDetail
TextView
element to the newSportsDetailTitle
style you created:
style="@style/SportsDetailTitle"
- Run your app. All of the text is now larger on the tablet, which greatly improves the user experience of your application.
5. Task 3: Localize your app
A "locale" represents a specific geographic, political or cultural region of the world. Resource qualifiers can be used to provide alternate resources based on the users' locale. Just as for orientation and screen width, Android provides the ability to include separate resource files for different locales. In this step, you modify your strings.xml
file to be a little more international.
3.1 Add a localized strings.xml file
You may have noticed that the sports information contained in this app is designed for users from the U.S. The app uses the term "soccer" to represent a sport known as "football" everywhere else in the world.
To make your app more internationalized, you can provide a locale-specific strings.xml
file. This alternative-resource file will show the word "soccer" to users in the U.S. The generic strings.xml
file will show the word "football" to users in all other locales.
- Create a new
values
resource file. - Call the file strings.xml and select Locale from the list of available qualifiers. The Language and Specific Region Only panes appear.
- In the Language pane, select en: English.
- In the Specific Region Only pane, select US: United States and click OK. Android Studio creates a specific
values
directory in your project directories for the U.S. locale, calledvalues-en-rUS
. In the Project > Android pane, thestrings.xml
file in this directory appears as strings.xml (en-rUS) within the newly created strings.xml folder (with a U.S. flag icon).
- Copy all string resources of the generic strings.xml file (now located in the strings.xml folder) to strings.xml (en-rUS).
- In the generic strings.xml file, change the Soccer item in the
sports_titles array
to Football, and change the Soccer news text in thesports_info
array to Football news.
3.2 Run the app in different locales
In order to see the locale-specific differences, you can start your device or emulator, and change its language and locale to U.S. English (if not already set). In U.S. English, you should see "Soccer". You can then switch to any language and locale other than U.S. English, and run the app again. You should then see "Football".
- To switch the preferred language in your device or emulator, open the Settings app.
If your Android device is in another language, look for the gear icon:
- Find the Languages & input settings in the Settings app, and choose Languages. Languages is the first choice on the Languages & input screen.
Remember the globe icon for the Languages & input choice, so that you can find it again if you switch to a language you do not understand:
- For devices and emulators running a version of Android previous to Android 7, choose Language on the Languages & input screen, select a language and locale such as Français (France), and skip the following steps.
(In versions of Android previous to Android 7, users can choose only one language. In Android 7 and newer versions, users can choose multiple languages and arrange them by preference. The primary language is numbered 1, as shown in the following figure, followed by lower-preference languages.)
- For devices and emulators running Android 7 or newer, choose Languages on the Languages & input screen, select a language such as Français (France), and use the move icon on the right side of the Language preferences screen to drag Français (France) to the top of the list.
- Run the app with your device or emulator. In U.S. English, you should see "Soccer".
- Switch to any language and locale other than U.S. English, and run the app again. You should then see "Football".
This example does not show a translated word for "Football" depending on the language. For a lesson in localizing an app with translations, see the Advanced Android Development — Practicals.
6. Solution code
Android Studio project: MaterialMe-Resource
7. Coding challenge
Challenge 1: It turns out that several countries other than the U.S. use "soccer" instead of "football". Research these countries and add localized strings resources for them.
Challenge 2: Use the localization techniques you learned in Task 3 in combination with Google translate to translate all of the strings in your app into a different language.
8. Summary
GridLayoutManager
is a layout manager that handles two-dimensional scrolling lists.- You can dynamically change the number of columns in a
GridLayoutManager
. - The Android runtime uses alternative configuration files, depending on the runtime environment of the device running your app. For example, the runtime might use alternative configuration files for different device layouts, screen dimensions, locale, countries, or keyboard types.
- In your code, you create these alternative resources for the Android runtime to use. The resources are located in files that have resource qualifiers as part of their names.
- The format for a directory holding alternative resource files is
<resource_name>
-
<qualifier>
. - You can qualify any file in your
res
directory in this way.
9. Related concepts
The related concept is in 5.3: Resources for adaptive layouts.
10. Learn more
Android Studio documentation: Meet Android Studio
Android developer documentation:
- App resources overview
- Providing alternative resources
- Localize your app
LinearLayoutManager
GridLayoutManager
- Screen compatibility overview
Material Design:
11. Homework
This section lists possible homework assignments for students who are working through this codelab as part of a course led by an instructor. It's up to the instructor to do the following:
- Assign homework if required.
- Communicate to students how to submit homework assignments.
- Grade the homework assignments.
Instructors can use these suggestions as little or as much as they want, and should feel free to assign any other homework they feel is appropriate.
If you're working through this codelab on your own, feel free to use these homework assignments to test your knowledge.
Build and run an app
Modify the RecyclerView app to use a GridLayoutManager
with the following column counts:
- For a phone: 1 column in portrait orientation, 2 columns in landscape orientation
- For a tablet: 2 columns in portrait orientation, 3 columns in landscape orientation
The screenshot below shows a resource-qualified RecyclerView
on a phone in portrait orientation:
The screenshot below shows a resource-qualified RecyclerView
on a phone in landscape orientation:
The screenshot below shows a resource-qualified RecyclerView
on a tablet in landscape orientation:
Answer these questions
Question 1
Which resource qualifier is used most frequently to select for tablets? Choose one:
- Orientation
- Screen width
- Screen height
- Smallest screen width
Question 2
Which folder would hold the strings.xml
file for translation into French for Canada? Choose one:
res/values-fr-rFR/
res/values-ca-rFR/
res/values-fr-rCA/
res/values-en-rFR/
Question 3
Which folder is for XML files that contain strings, integers, and colors? Choose one:
res/layout
res/mipmap
res/raw
res/values
Submit your app for grading
Guidance for graders
Check that the app has the following features:
- For phones and tablets in both landscape and portrait modes, the code includes resource-qualified
values
files that contain the integer for the column count. - The app uses
getResources().getInteger()
to retrieve a value from a resource file, then uses the value as the column count for grid layout.
12. Next codelab
To find the next practical codelab in the Android Developer Fundamentals (V2) course, see Codelabs for Android Developer Fundamentals (V2).
For an overview of the course, including links to the concept chapters, apps, and slides, see Android Developer Fundamentals (Version 2).