App resources are all additional files and static content within your app that explain your services and show your unique brand such as strings, bitmaps, layout definitions, menus, animations, and others. To provide a customized user experience, it's usually necessary to have different resources based on user languages, countries, screen orientations, and many other device-specific configurations. However, trying to handle these resource variations manually in the code can be a time-consuming and messy task.
The Android framework provides you with a built-in functionality that lets you decouple (separate) your app resources from the code so that you can maintain them independently and switch between them using predefined rules without having to touch the code.
How Android supports varying resources
Let's use an example to explain the concept. Imagine you have an app that you want to make available in multiple languages, in addition to English. How can you achieve that? One initial solution that might come to mind is to use if statements in your code to select the languages for the displayed text.
However, if you implement this logic every time you add text, your codebase will become messy and confusing. Instead, you can take a different approach. Create variables that represent all the text in your app, and group these variables in one file for each language. For example, one file could contain variables for English, another file for French, and so on.
When the app is launched, it only needs to check the language preference once at the beginning. Once the preferred language is known, the app can simply retrieve the variables from the corresponding language file and ignore other language files. This second approach reduces code clutter and lets you write concise code making it easier to localize your app for multiple languages.
// Language preference obtained from user settings or device locale.
preferredLanguage = getPreferredLanguage()
// Load language-specific resource file based on the preferred language.
resourceFile = loadResourceFile(preferredLanguage)
// Use the retrieved text in your app's UI.
// Each file contains the same keys, but with different values.
displayButton(resourceFile.getButtonText())
displayTitle(resourceFile.getTitleLabel())
displayMessage(resourceFile.getMessageText())
The Android framework implements a similar approach for you and handles its logic. This means you can concentrate on writing concise code for your business logic without having to worry which resources will be shown in different scenarios. To access this functionality, you simply need to provide alternative resources and organize them into predefined named resource directories from which the Android framework can access and retrieve resources from.
Resource qualifiers
Resources need to be organized in specific directories with predefined names for Android to know which to choose and display in different scenarios. Those special directory names are known as qualifiers as they qualify the resources to be selected and displayed instead of the default resources. Following up on our example of localizing your app, here is the resource directories tree that includes valid qualifiers for different languages:
res/
├── drawable/
│ └── ...
├── layout/
│ └── ...
├── values/ # Default resources for Values
│ ├── strings.xml # Strings in English
│ └── ...
├── values-es/ # Values Resources for the Spanish language qualifier
│ ├── strings.xml # Strings in Spanish
│ └── ...
├── values-fr/ # Values Resources for the French language qualifier
│ ├── strings.xml # Strings in French
│ └── ...
└── ...
And here is a snapshot from the English file values/strings.xml:
<resources>
<string name="app_name">My App</string>
<string name="toast_message">Welcome to My App!</string>
<string name="button1_text">Click Me</string>
------
</resources>
This file is the default file which means that strings would be pulled from it in all cases except only when the user's system languages are Spanish or French as these two have specific resource files defined for them with language qualifiers es and fr respectively.
And here is a snapshot from the Spanish file values-es/strings.xml:
<resources>
<string name="app_name">Mi Aplicación</string>
<string name="toast_message">¡Bienvenido a Mi Aplicación!</string>
<string name="button1_text">Haz Clic</string>
------
</resources>
As you can see, the string resources for all translation files should have the same names, but with different values relevant to each locale.
Screen orientation qualifiers
You can improve your app's user experience by using screen orientation qualifiers. These qualifiers allow you to create specific resources customized for each orientation (landscape and portrait). This means your app's user interface will automatically adjust to provide the best experience depending on how the device is held or rotated.
res/
├── drawable/
│ └── ...
├── values/
│ └── ...
├── layout/ # Default Layout resource files
│ ├── activity_main.xml
│ └── ...
├── layout-land/
│ ├── activity_main.xml # Layout resource files for landscape orientation
│ └── ...
├── layout-port/
│ ├── activity_main.xml # Layout resource files for portrait orientation
│ └── ...
└── ...Screen size qualifiers
Screen size qualifiers categorize Android devices based on their screen sizes. They are divided into 4 categories to provide optimized layouts and alternative resources for the different screen sizes. This ensures an optimized user experience on various Android devices. Here are the categories and their approximate minimum layout sizes:
- small: similar size to a low-density QVGA screen (320x426 dp units).
- normal: similar size to a medium-density HVGA screen (320x470 dp units).
- large: similar size to a medium-density VGA screen (480x640 dp units).
- xlarge: larger than a medium-density HVGA screen (720x960 dp units).
Screen pixel density qualifiers
Using pixel density qualifiers, you can provide alternative image resources optimized for varying screen pixel densities. This ensures that the visuals within your app always appear crisp and clear on devices with various pixel densities. Here are the popular screen pixel density qualifiers:
- ldpi: for low-density screens (120 dpi).
- mdpi: for medium-density screens (160 dpi).
- hdpi: for high-density screens (240 dpi).
- xhdpi: for extra-high-density screens (320 dpi).
- xxhdpi: for extra-extra-high-density screens (480 dpi).
- xxxhdpi: for extra-extra-extra-high-density screens (640 dpi).
Languages and regions qualifiers
In addition to localizing your app for different languages as explained at the beginning of the topic, you can also provide a more customized experience based on regions or countries. For example, if you want to cater to users in Canada in a way that's different from users in the United States, you can use language qualifiers combined with region qualifiers. This allows you to offer a more native and personalized user experience for specific regions within the same language. Have a look at the ISO 639-1 for language code, and ISO 3166-1-alpha-2 for region code. Here is an example showing the use of language and region qualifiers:
res/
├── values-en-rUS/ # Values Resources for the English language in the US
│ ├── strings.xml # Strings in for English American
│ └── ...
├── values-en-rCA/ # Values Resources for the English language in Canada
│ ├── strings.xml # Strings in for English Canadian
│ └── ...
├── values-fr-rCA/ # Values Resources for the French language in Canada
│ ├── strings.xml # Strings in for French Canadian
│ └── ...
└── ...Android Studio
So many qualifiers with various values look like a lot to memorize, doesn't it? Don't worry, Android Studio is there to take care of such cumbersome tasks. It even provides customized workflows specific to each type of resource. These workflows simplify the process of adding qualifiers. Refer to the official documentation to know more about Android Studio workflows for adding different app resources. But for now, the general workflow for adding any type of resource would be similar to what you can see below.
Here is a paraphrased version of the given instructions:
- Go to the Project window in either the Android or Project view and click on the target app module.
- From the top menu, choose File, then select New, and then click on Android resource file.
- A dialog box titled "New Resource File" will appear where you need to provide the necessary details (File name, Resource type.)
- Available qualifiers: In the dialog box, you can see a table with all qualifiers that you can make use of. Instead of manually modifying the directory name, you can add configuration qualifiers by selecting the required qualifiers from the list and clicking on the
button. Once added, you'll be prompted to choose the corresponding value for the selected qualifier. For instance, if you opt for the Density qualifier, you'll be presented with a selection of various values associated with it. - Once you have included all the desired qualifiers, click on the OK button in the dialog box.
Next steps
You can combine various qualifiers to create even more specific resources that target devices meeting all the combined qualifiers at once, but you have to write them in a specific order, otherwise, they would be ignored. So, it's better just to create them with Android Studio. Refer to the documentation to know more about Qualifier name rules. You can also go ahead and let Android Studio create them for you.
When you need to utilize a resource for multiple qualifiers, there's no need to duplicate it in multiple resource directories. Instead, you can create an alias resource that serves as a reference to the original resource. Check the documentation to know more about alias resources.
Conclusion
App resources are an integral part of the UI/UX, including strings, colors, images, layouts, and more. The Android framework allows you to make them loosely coupled from the code by organizing them into predefined named directories that are called qualifiers. There are various qualifiers for language, screen orientation, screen size, pixel density, and other parameters to help you select and display the appropriate resources in different scenarios.