How Android Determines Which Resources to Pick Up
It is a bad idea to mix content with code, and resources are no different. The application that you first created for the US market might become the tomorrow's bestseller. If you haven't decoupled resources from code, you will not be able to sell to other markets until you have localized it, and will have to pay the tax to extract the resources and re-architect the code base. Why not start with the right way to put resources in your Android applications?
In an Android system, resources are accessed by the identifiers, called resource IDs, that are generated in a project's R class. Depending on the type of Android system resources, they would be stored at different locations in the project folder hierarchy. A typical folder structure is as follows:
The preceding is just an example. There are many more types of resources and they all will be residing under the /res folder under the project name.
Here is a list of all possible resource folders that can exist under the /res folder:
- /animator/: Property animations in XML format
- /anim/: Tween animations in XML format. Property animations can also go here, although /animator/ folder is preferred.
- /color/: List of colors in XML format
- /drawable/: Drawable resources in bitmap or XML format
- /mipmap/: Drawable files for different launch icon densities
- /layout/:Layout files for activities in XML format
- /menu/: Application menus (like the Content and Options menus) in XML format
- /raw/: Folder for arbitrary resources in their raw form
- /values/: Various values in XML format. One difference from other folders is that content in XML files in other other folders have one type of content, whereas the XML file in /values/ folder can have values for different content types.
Resources to Enhance the User Experience
Android supports the ability to provide alternate resources to give users a differentiated experience from the default experience. These alternate resources can be provided for one or more of the categories listed above (under the resource folder list).
For example, if app developers want to provide richer graphics for HDPI devices, they can choose to offer these resources under the "/drawable-hdpi/" folder and provide the default resources under the "/drawable/" folder.
As evident from the folder name, the "hdpi" annotation to the folder name is interpreted by the Android platform to be lighted up only when the device meets the specification. For a normal device, the resources from /drawable/ folder will be picked. However, for a device with high-density screen, the resources will be picked from the /drawable-hdpi/ folder, if one exists.
There are a number of qualifier that can be used for resources. These qualifiers are listed below.
- MCC and MNC (mobile country code and mobile network code): These resources are picked up from the SIM card. These are used by operating systems to show the network provider name in the Phone settings.
- Language and region (for example, en-US: The format is the two-letter ISO 639-1 country code with optional two-letter ISO 3166-1 region code.
- Layout direction (for example, ldrtl or ldltr): This represents the layout direction. The ldrtl is used for layouts which are from right to left (like Arabic regions).
- Smallest width (format sw<N>dp): This represents the fundamental size of the screen irrespective of orientation. This is used for layout. For example, an application can provide different layout resources for a screen which sports a minimum of 600 pixels (for example, /layout-sw600dp/).
- Available width (format: w<N>dp): This represents the minimum available width accounting for orientation.
- Available height (format: h<N>dp): This represents the minimum available height accounting for orientation.
- Screen size (values: small/normal/large/xlarge): This represents the size of the screen,
- Screen aspect (values: long/notlong): Represents the aspect ratio of the screen.
- Round screen (values: round/notround): Self explanatory.
- Screen orientation (values: port/land): Self explanatory.
- UI mode (values: values: car/desk/television/appliance/watch): Represents the UI mode of the device.
- Night mode (values: night/notnight): Self explanatory.
- Screen pixel density.
- Touchscreen type (values: notouch/finger): Self explanatory.
- Keyboard availability (values: keysexposed/keyshidden/keyssoft): Self explanatory.
- Primary text input method (values: nokeys/qwerty/12key): Self explanatory.
- Navigation key availability (values: navexposed/navhidden): Represents whether navigation keys are available to the user.
The qualifiers above follow certain rules for ordering:
- Multiple qualifiers can be provided if they are separated by dashes.
- The qualifiers have to be in the order listed above.
- Alternate resource folders aren't nested; they are siblings of the main resource folders.
- Multiple values for qualifiers are not allowed in a resource folder.
Let's create a simple Android application that demonstrates how to provide resources.
Fire up Android Studio and Start a new Android Studio Project.
Figure 1: Starting a new Android Studio project
Provide ProvidingResourcesDemo as the Application Name and Click Next.
Figure 2: Providing an application name
On the next screen, leave the default values and click Next.
Figure 3: Leaving the default values in place
On the "Add an activity to Mobile" page, choose "Blank Activity". This creates an application with a single activity.
Figure 4: Adding an activity to the Mobile page
We are then prompted to customize the activity. We will leave the default values unchanged.
Figure 5: Again, leaving the default values unchanged
Click Finish to creating the project files.
You can observe the layout of the resources in the Project view.
Figure 6: Observing the resources' layout
We will now add alternate resources for layout when the orientation is portrait. Right-click the "Res" folder and select New -> Android Resource Directory.
Figure 7: Creating a new resource file
This will create a folder called /layout-port/ under /res. Copy the activity_main.xml file from the /layout/ folder into this folder. Next, open the /layout-port/activity_main.xml file and make the following changes:
<RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:text="@string/hello_world_port" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
Note that string/hello_world_port does not exist yet. So, you will see an error if you compile the application now.
We now create another resource directory for alternate layout resources for landscape mode. Following similar steps as above and choosing Landscape orientation, we will now have a /layout-land/ folder under /res.
Figure 9: Making a Landscape directory
Copy the activity_main.xml file from the /layout/ folder into this folder. Next, open the /layout-land/activity_main.xml file and make the following changes:
<RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <TextView android:text="@string/hello_world_land" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout>
Finally, we create the necessary string resources in the /values/ folder for the two string resources we introduced above.
Open up /values/strings.xml and make the following changes:
<resources> <string name="app_name">ProvidingResourcesDemo</string> <string name="hello_world">Hello world!</string> <string name="hello_world_port">Hello world in Portrait!</string> <string name="hello_world_land">Hello world in Landscape!</string> <string name="action_settings">Settings</string> </resources>
Our simple application to demonstrate how resource fallback works is now ready. Start the application. Once the application launches, you will see that it picks up the hello_world_port string.
Figure 10: Showing the "Hello world" text in Portrait mode
When we flip the emulator to landscape mode (Ctrl +F11), we can notice that the string displayed on the screen has also changed.
Figure 11: Showing the "Hello world" text in Landscape mode
This is a working example of alternate resource working in action. We can create even more advanced scenarios on alternate resources. That exercise is left to the reader.
For more information on this topic, please see the previous article in this series.
In this article, we learned thee fundamentals of how resources work on the Android platform and we built a sample application that illustrates working with alternate resources. You can download the same code from the link at the end of this article.
About the Author
Vipul Patel is a technology geek based in Seattle. He can be reached at firstname.lastname@example.org. You can visit his LinkedIn profile at https://www.linkedin.com/pub/vipul-patel/6/675/508.