[Android Software Development] michael.the.don:
[Ultimate Guide][Part 4-1/09/2011]Android App Development - From Scrap

PART IV

Resources

As well as the resources you create, Android supplies several system resources that you can use in your applications. The resources can be used directly from your application code and can also be referenced from within other resources.

Using Resources in Code

You access resources in code using the static R class. R is a generated class based on your external resources and created by compiling your project. The R class contains static subclasses for each of the resource types for which you’ve defi ned at least one resource. For example, the default new project includes the R.string and R.drawable subclasses.

If you are using the ADT plug-in in Eclipse, the R class will be created automatically when you make any change to an external resource fi le or folder. If you are not using the plug-in, use the AAPT tool to compile your project and generate the R class. R is a compiler-generated class, so don’t make any manual modifi cations to it as they will be lost when the fi le is regenerated.

Each of the subclasses within R exposes its associated resources as variables, with the variable names matching the resource identifi ers — for example, R.string.app_name or R.drawable.icon. The value of these variables is a reference to the corresponding resource’s location in the resource table, not an instance of the resource itself.

Where a constructor or method, such as setContentView, accepts a resource identifi er, you can pass in the resource variable, as shown in the code snippet below:

// Inflate a layout resource.

setContentView(R.layout.main);

// Display a transient dialog box that displays the

// error message string resource.

Toast.makeText(this, R.string.app_error, Toast.LENGTH_LONG).show();

When you need an instance of the resource itself, you’ll need to use helper methods to extract them from the resource table, represented by an instance of the Resources class. Because these methods perform lookups on the application’s resource table, these helper methods can’t be static. Use the getResources method on your application context as shown in the snippet below to

access your application’s Resource instance:

Resources myResources = getResources();

The Resources class includes getters for each of the available resource types and generally works by passing in the resource ID you’d like an instance of. The following code snippet shows an example of using the helper methods to return a selection of resource values:

Resources myResources = getResources();

CharSequence styledText = myResources.getText(R.string.stop_message);

Drawable icon = myResources.getDrawable(R.drawable.app_icon);

int opaqueBlue = myResources.getColor(R.color.opaque_blue);

float borderWidth = myResources.getDimension(R.dimen.standard_border);

Animation tranOut;

tranOut = AnimationUtils.loadAnimation(this, R.anim.spin_shrink_fade);

String[] stringArray;

stringArray = myResources.getStringArray(R.array.string_array);

int[] intArray = myResources.getIntArray(R.array.integer_array);

Frame-by-frame animated resources are inflated into AnimationResources. You can return the value using getDrawable and casting the return value as shown below:

AnimationDrawable rocket;

rocket = (AnimationDrawable)myResources.getDrawable(R.drawable.frame_by_frame);

Referencing Resources in Resources

You can also reference resources to use as attribute values in other XML resources. This is particularly useful for layouts and styles, letting you create specialized variations on themes and localized strings and graphics. It’s also a useful way to support different images and spacing for a layout to ensure that it’s optimized for different screen sizes and resolutions.

To reference one resource from another, use @ notation, as shown in the following snippet:

attribute=”@[packagename:]resourcetype/resourceidentifier”

The following snippet creates a layout that uses color, dimension, and string resources:

<?xml version=”1.0” encoding=”utf-8”?>

<LinearLayout

xmlns:android=”http://schemas.android.com/apk/res/android”

android:orientation=”vertical”

android:layout_width=”fill_parent”

android:layout_height=”fill_parent”

android:padding=”@dimen/standard_border”>

<EditText

android:id=”@+id/myEditText”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:text=”@string/stop_message”

android:textColor=”@color/opaque_blue”

/>

</LinearLayout>

Using System Resources

The native Android applications externalize many of their resources, providing you with various strings, images, animations, styles, and layouts to use in your applications. Accessing the system resources in code is similar to using your own resources. The difference is that you use the native android resource classes available from android.R, rather than the application-specifi c R

class. The following code snippet uses the getString method available in the application context to retrieve an error message available from the system resources:

CharSequence httpError = getString(android.R.string.httpErrorBadUrl);

To access system resources in XML, specify Android as the package name, as shown in this XML snippet:

<EditText

android:id=”@+id/myEditText”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:text=”@android:string/httpErrorBadUrl”

android:textColor=”@android:color/darker_gray”

/>

Referring to Styles in the Current Theme

Themes are an excellent way to ensure consistency for your application’s UI. Rather than fully define each style, Android provides a shortcut to let you use styles from the currently applied theme.

To do this, you use ?android: rather than @ as a prefix to the resource you want to use. The following example shows a snippet of the above code but uses the current theme’s text color rather than an external resource:

<EditText

android:id=”@+id/myEditText”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:text=”@string/stop_message”

android:textColor=”?android:textColor”

/>

This technique lets you create styles that will change if the current theme changes, without having to

modify each individual style resource.

Creating Resources for Different Languages and Hardware

One of the most powerful reasons to externalize your resources is Android’s dynamic resource selection mechanism.

Using the structure described below, you can create different resource values for specifi c languages, locations, and hardware confi gurations that Android will choose between dynamically at run time. This lets you create language-, location-, and hardware-specifi c user interfaces without having to change your code.

Specifying alternative resource values is done using a parallel directory structure within the res/ folder, using hyphen (-) separated text qualifi ers to specify the conditions you’re supporting. The example hierarchy below shows a folder structure that features default string values, along with a French language alternative with an additional Canadian location variation:

Project/

res/

values/

strings.xml

values-fr/

strings.xml

values-fr-rCA/

strings.xml

The following list gives the available qualifiers you can use to customize your resource files:

❑ Language Using the lowercase two-letter ISO 639-1 language code.

❑ Region A lowercase “r” followed by the uppercase two-letter ISO 3166-1-alpha-2 language code.

❑ Screen Orientation One of port (portrait), land (landscape), or square (square)

❑ Screen Pixel Density Pixel density in dots per inch (dpi)

❑ Touchscreen Type One of notouch, stylus, or finger

❑ Keyboard Availability Either of keysexposed or keyshidden

❑ Keyboard Input Type One of nokeys, qwerty, or 12key

❑ UI Navigation Type One of notouch, dpad, trackball, or wheel

❑ Screen Resolution Screen resolution in pixels with the largest dimension first

You can specify multiple qualifiers for any resource type, separating each qualifier with a hyphen. Any combination is supported; however, they must be used in the order given in the list above, and no more than one value can be used per qualifier.

The following example shows valid and invalid directory names for alternative drawable resources.

❑ Valid:

drawable-en-rUS

drawable-en-keyshidden

drawable-land-notouch-nokeys-320x240

❑ Invalid:

drawable-rUS-en (out of order)

drawable-rUS-rUK (multiple values for a single qualifier)

Runtime Configuration Changes

Android supports runtime changes to the language, location, and hardware by terminating and restarting each application and reloading the resource values. This default behavior isn’t always convenient or desirable, particularly as some configuration changes (like screen orientation and keyboard visibility) can occur as easily as a user rotating the device or sliding out the keyboard. You can customize your application’s response to these changes by detecting and reacting to them yourself.

To have an Activity listen for runtime configuration changes, add an android:configChanges attribute to its manifest node, specifying the configuration changes you want to handle.

The following list describes the configuration changes you can specify:

❑ orientation The screen has been rotated between portrait and landscape.

❑ keyboardHidden The keyboard has been exposed or hidden.

❑ fontScale The user has changed the preferred font size.

❑ locale The user has chosen a different language setting.

❑ keyboard The type of keyboard has changed; for example, the phone may have a 12 keypad

that flips out to reveal a full keyboard.

❑ touchscreen or navigation The type of keyboard or navigation method has changed. Neither

of these events should normally happen.

You can select multiple events to handle by separating the values with a pipe (|).

The following XML snippet shows an activity node declaring that it will handle changes in screen orientation

and keyboard visibility:

<activity android:name=”.List”

android:label=”@string/app_name”

android:theme=”@style/ListTheme”

android:configChanges=”orientation|keyboard”/>

Adding this attribute suppresses the restart for the specified configuration changes, instead, triggering the onConfigurationChanged method in the Activity. Override this method to handle the configuration changes using the passed-in Configuration object to determine the new configuration values, as shown in the following skeleton code. Be sure to call back to the super class and reload any resource values that the Activity uses in case they’ve changed.

@Override

public void onConfigurationChanged(Configuration _newConfig) {

super.onConfigurationChanged(_newConfig);

[ ... Update any UI based on resource values ... ]

if (_newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {

[ ... React to different orientation ... ]

}

if (_newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) {

[ ... React to changed keyboard visibility ... ]

}

}

When onConfigurationChanged is called, the Activity’s Resource variables will have already been updated with the new values so they’ll be safe to use. Any configuration change that you don’t explicitly flag as being handled by your application will still cause an application restart without a call to onConfigurationChanged.

Activity

To create a new Activity, you extend the Activity class, defining the user interface and implementing your functionality. The basic skeleton code for a new Activity is shown below:

package com.paad.myapplication;

import android.app.Activity;

import android.os.Bundle;

public class MyActivity extends Activity {

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

}

}

The base Activity class presents an empty screen that encapsulates the window display handling functionality. An empty Activity isn’t particularly useful, so the first thing you’ll want to do is lay out the screen interface using Views and layouts. Activity UIs are created using Views. Views are the user-interface controls that display data and provide user interaction. Android provides several layout classes, called View Groups, that can contain multiple Views to help you design compelling user interfaces.

To assign a user interface to an Activity, call setContentView from the onCreate method of your Activity.

In this first snippet, a simple instance of MyView is used as the Activity’s user interface:

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

MyView myView = new MyView(this);

setContentView(myView);

}

More commonly you’ll want to use a more complex UI design. You can create a layout in code using layout View Groups, or you can use the standard Android convention of passing a resource ID for a layout defined in an external resource, as shown in the snippet below:

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.main);

}

In order to use an Activity in your application, you need to register it in the manifest. Add new activity tags within the application node of the manifest; the activity tag includes attributes for metadata such as the label, icon, required permissions, and themes used by the Activity. An Activity without a corresponding activity tag can’t be started. The following XML snippet shows how to add a node for the MyActivity class created in the snippets above:

<activity android:label=”@string/app_name”

android:name=”.MyActivity”>

<intent-filter>

<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</activity>

Within the activity tag, you can add intent-filter nodes that specify the Intents your Activity will listen for and react to. Each Intent Filter defi nes one or more actions and categories that your Activity supports. make an Activity available from the main program launcher, it must include an Intent Filter listening for the Main action and the Launcher category, as highlighted in the snippet below:

<activity android:label=”@string/app_name”

android:name=”.MyActivity”>

<intent-filter>

<action android:name=”android.intent.action.MAIN” />

<category android:name=”android.intent.category.LAUNCHER” />

</intent-filter>

</activity>

Activity States

As activities are created and destroyed, they move in and out of the stack. As they do so, they transition through four possible states:

❑ Active When an Activity is at the top of the stack, it is the visible, focused, foreground activity that is receiving user input. Android will attempt to keep it alive at all costs, killing Activities further down the stack as needed, to ensure that it has the resources it needs. When another Activity becomes active, this one will be paused.

❑ Paused In some cases, your Activity will be visible but will not have focus; at this point, it’s paused. This state is reached if a transparent or non-full-screen Activity is active in front of it. When paused, an Activity is treated as if it were active; however, it doesn’t receive user input events. In extreme cases, Android will kill a paused Activity to recover resources for the active

Activity. When an Activity becomes totally obscured, it becomes stopped.

❑ Stopped When an Activity isn’t visible, it “stops.” The Activity will remain in memory retaining all state and member information; however, it is now a prime candidate for execution when the system requires memory elsewhere. When an Activity is stopped, it’s important to save data and the current UI state. Once an Activity has exited or closed, it becomes inactive.

❑ Inactive After an Activity has been killed, and before it’s been launched, it’s inactive. Inactive Activities have been removed from the Activity stack and need to be restarted before they can be displayed and used.

State transitions are nondeterministic and are handled entirely by the Android memory manager. Android will start by closing applications that contain inactive Activities, followed by those that are stopped, and in extreme cases, it will remove those that are paused. To ensure a seamless user experience, transitions between these states should be invisible to the user. There should be no difference between an Activity moving from paused, stopped, or killed states back to active, so it’s important to save all UI state changes and persist all data when an Activity is paused or stopped. Once an Activity does become active, it should restore those saved values