There are many things to worry about when designing and developing for Android devices. Some of the most common questions a developer may have when entering the world of Android development are: How do we support all the different device sizes? How do we support device rotation layouts? How do we support different locales and languages?
Fortunately, Android gives us the ability to easily address these issues. Most developers have had experience using the different resource directories in their projects. For the most part, we know that images must be placed in the drawable directory, layouts in the layouts directory, and values in the values directory. Things start to get tricky when we need to accomplish more specific tasks, such as supporting high-resolution images for certain devices, or separate layouts for landscape and portrait. In order to address these unique issues, we need to create different folders for these resources. By grouping them in specifically-named folders with appropriate qualifier tags, Android will then use the appropriate resource at runtime, based on the user’s device configuration.
One of the problems I occasionally come across while using Android apps is that I see images that are blurry, pixelated, or stretched awkwardly. The solution to this problem is to create a drawable folder for each of the densities that Android supports, and place the correctly scaled image inside each folder. As of writing, there are 6 different density ranges (measured in dots per inch) that Android supports:
- ldpi: Low-density screens; approximately 120dpi
- mdpi: Medium-density (on traditional HVGA) screens; approximately 160dpi
- hdpi: High-density screens; approximately 240dpi
- xhdpi: Extra high-density screens; approximately 320dpi. Added in API Level 8
- nodpi: This can be used for bitmap resources that you do not want to be scaled to match the device density
- tvdpi: Screens somewhere between mdpi and hdpi; approximately 213dpi. This is not considered a “primary” density group. It is mostly intended for televisions and most apps shouldn’t need it—providing mdpi and hdpi resources is sufficient for most apps and the system will scale them as appropriate. This qualifier was introduced with API level 13.
For current phone projects, every project should have at least 4 folders for drawables (drawable-ldpi, drawable-mdpi, drawable-hdpi and drawable-xhdpi). This allows images to be crisp and clear for all Android devices, as they won’t need to be stretched. If you are short on time, however, I have experienced that just having the xhdpi folder with assets works almost as well. These assets will be scaled down automatically on lower density devices. Keep in mind though, that low-density devices will be processing a resource that is much larger than it needs, which means it will take up more of the device’s memory.
Similarly, qualifiers exist for device orientation (port/land), platform version (v7, v8, v11, v15, etc…), language (en, fr) and many others. A full list of configuration qualifier names that can be used in Android resource folders are listed at http://developer.android.com/guide/topics/resources/providing-resources.html#table2.
So what if you want to display a different image on a high resolution device that is set to French? Short answer: create a directory in the res/ folder named drawable-fr-hdpi and place your image inside that folder. Keep in mind though, there are rules to the ordering of the qualifiers that you use for your directories. Android will search top-down based on the table shown in the link above, so since locale has a higher precedence than screen density in the table, a folder named drawable-hdpi-fr will cause an error, while drawable-fr-hdpi will be fine.
Once we have the structure set up and all assets in place, how do we use them in our project?
There are two ways that you access resources; either through code or in XML.
When you reference the resource, you shouldn’t include the qualifier(s). For example, to set the text of a TextView to a string that is located in your resources, you can use:
yourTextView.setText(R.string.your_string) in your code
or android:text=”@string/your_string” in your XML file
Even if ‘your_string’ was located in the ‘values-fr’, ‘values-en’, or ‘values-zh’, you must only use R.string.your_string, and let Android take care of the rest.
With a greater understanding of Android resource folders, you will no longer have the need to try and detect all the device properties programmatically in order to display appropriate assets/data. Using the folders properly will allow you to easily support multiple screen densities, sizes, languages and orientations quite easily and will save a lot of time and effort when done correctly.