Additional Android Resource Types

28 Aug 2013

Most Android developers associate the values resources folder with simple string elements in strings.xml. But buried in the Android documentation are some additional resource types that not only simplify development, but help make your application look professional. Let’s look at a few.

Dimensions

In a file named layout.xml, we can define dimension values using the dimen element:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="margin_large">24dp</dimen>
    <dimen name="margin_small">12dp</dimen>
    <dimen name="button_small_height">36dp</dimen>
    <dimen name="button_large_height">52dp</dimen>
    <dimen name="icon_size">32dp</dimen>
</resources>

Note that we’re using dp, or density-independent pixels, instead of sp. It is recommended that you use the latter only when specifying font sizes, and the former in all other cases. Once we’ve defined these values, we can reference them in layout XML files by using the prefix @dimen. For example, the following ImageView element in a RelativeLayout defines its width and height as icon_size, and its right margin as margin_small:

<ImageView
    android:id="@+id/presence_icon"
    android:layout_width="@dimen/icon_size"
    android:layout_height="@dimen/icon_size"
    android:layout_marginRight="@dimen/margin_small"
    ...
    />

By defining such dimensional values in layout.xml, you ensure consistent alignment and spacing across screens. This gives your application a tighter look and a more professional appearance.

For additional information on dimen elements, see the Android documentation.

Colors

In a file named colors.xml, we can define color values using the color element:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="app_text">#464646</color>
    <color name="app_text_dark">#2E2E2E</color>
    <color name="app_warning">#E45620</color>
</resources>

Colors defined in colors.xml are helpful when we must change the color of a view based on data known only at run-time. For example, the following code changes the color of the displayed credits to app_warning if their number is below a certain threshold. It selects this color through the getColor method of a Resources object, which is returned from the getResources() method of an Activity instance:

if (creditsRemaining < CREDITS_WARNING_THRESHOLD) {
  int warningColor = getResources().getColor(R.color.app_warning);
  creditsRemainingView.setTextColor(warningColor);
}

We can also reference these values in a layout XML file using the prefix @color:

<TextView
    android:id="@+id/title"
    android:textColor="@color/app_text_dark"
    android:textStyle="bold"
    ...
    />

Doing this is less common. The primary colors of your app should be defined in a theme and therefore applied automatically. And similar to dimensions, by defining such colors in colors.xml, you ensure consistent coloring across screens for a more professional appearance.

For additional information on color elements, see the Android documentation.

Plurals

Finally, let’s touch on a useful feature of strings.xml: You can define plural forms of strings, thereby avoiding embarrassing grammar mistakes.

As a motivating example, consider the following string element in strings.xml:

<string name="shop_prompt">Purchasing %1$s costs %2$d credits</item>

Given a ShopItem instance with a string title field and an integer credits field, say we format this string by doing:

String prompt = getString(
    R.string.shop_prompt, shopItem.title, shopItem.credits);

If shopItem.title equals "Product A" and shopItem.credits equals 1, then this assigns the grammatically incorrect string "Purchasing Product A costs 1 credits" to prompt. In fact, the quantity of 1 is the only grammatical irregularity we must guard against in the English language:

Knowing this, one solution is to define two string elements in strings.xml, where one is used for the quantity of 1, and the other is used for all other quantities:

<string name="shop_prompt_one">Purchasing %1$s costs %2$d credit</item>
<string name="shop_prompt_other">Purchasing %1$s costs %2$d credit</item>

We can then select and format the grammatically correct string by doing:

String prompt = null;
if (shopItem.credits == 1) {
  prompt = getString(
      R.string.shop_prompt_one, shopItem.title, shopItem.credits);
} else {
  prompt = getString(
      R.string.shop_prompt_other, shopItem.title, shopItem.credits);
}

This solution, however, has several drawbacks:

The plurals element

Using the plurals element addresses these drawbacks. It contains quantity strings, which are the different grammatical forms of a string for different quantities. Each quantity string is defined in an item element that is a child of the plurals element. A quantity attribute of each item element specifies for what quantities its quantity string should be used. For English, the only two relevant attribute values are "one" and "other", but sometimes "zero" is used. The "other" value is like the default label of a switch construct, and is used when the given quantity does not match the quantity attribute belonging to any other item element.

We can rewrite our two string elements in strings.xml as:

<plurals name="shop_prompt">
  <item quantity="one">Purchasing %1$s costs %2$d credit</item>
  <item quantity="other">Purchasing %1$s costs %2$d credits</item>
</plurals>

In a Java source file, we select an appropriate quantity string through the getQuantityString method of a Resources object:

String prompt = getResources().getQuantityString(
    R.plurals.shop_prompt, shopItem.credits, shopItem.title, shopItem.credits);

Note that while we typically select a string element with R.string, we select a plurals element with R.plurals, as we do here with R.plurals.shop_prompt. The second parameter, shopItem.credits, is the quantity evaluated to select the appropriate item element and its quantity string. The third and fourth parameters, shopItem.title and shopItem.credits, are the values substituted into the selected quantity string.

Above, if shopItem.title equals "Product A" and shopItem.credits equals 1, then the item element with quantity="one" is selected, and "Purchasing Product A costs 1 credit" is assigned to prompt. But if shopItem.credits equals 5 instead, then the item element with quantity="other" is selected, and "Purchasing Product A costs 5 credits" is assigned to prompt.

Other recognized quantity values include "two", "few", and "many". These are used in the aforementioned grammatical rules for languages other than English. Consult the Android documentation for more information.

Dealing with irregular formats

Note that all quantity strings contained in a plurals element must contain the same format specifiers in the same order. For example, you cannot do:

<plurals name="shop_prompt">
  <item quantity="zero">%1$s is free!</item>
  <item quantity="one">Purchasing %1$s costs %2$d credit</item>
  <item quantity="other">Purchasing %1$s costs %2$d credits</item>
</plurals>

If shopItem.credits equals 0, then getQuantityString will throw an exception at runtime because the second string formatting argument, shopItem.credits, has no corresponding format specifier %2$d in the string. Instead, this string should be moved to its own string element:

<string name="shop_prompt_zero">%1$s is free!</item>
<plurals name="shop_prompt">
  <item quantity="one">Purchasing %1$s costs %2$d credit</item>
  <item quantity="other">Purchasing %1$s costs %2$d credits</item>
</plurals>

Now select the string shop_prompt_zero when shopItem.credits equals 0, and the correct quantity string from shop_prompt when shopItem.credits is any other value:

String prompt = null;
if (shopItem.credits == 0) {
  prompt = getString(R.string.shop_prompt_zero, shopItem.title);
} else {
  prompt = getResources().getQuantityString(
      R.plurals.shop_prompt, shopItem.credits, shopItem.title, shopItem.credits);
}

For additional information on plural elements, see the Android documentation.

comments powered by Disqus