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

PART V

Monitoring State Changes

To ensure that Activities can react to state changes, Android provides a series of event handlers that are fired when an Activity transitions through its full, visible, and active lifetimes. The following skeleton code shows the stubs for the state change method handlers available in an Activity.

package com.paad.myapplication;

import android.app.Activity;

import android.os.Bundle;

public class MyActivity extends Activity {

// Called at the start of the full lifetime.

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

// Initialize activity.

}

// Called after onCreate has finished, use to restore UI state

@Override

public void onRestoreInstanceState(Bundle savedInstanceState) {

super.onRestoreInstanceState(savedInstanceState);

// Restore UI state from the savedInstanceState.

// This bundle has also been passed to onCreate.

}

// Called before subsequent visible lifetimes

// for an activity process.

@Override

public void onRestart(){

super.onRestart();

// Load changes knowing that the activity has already

// been visible within this process.

}

// Called at the start of the visible lifetime.

@Override

public void onStart(){

super.onStart();

// Apply any required UI change now that the Activity is visible.

}

// Called at the start of the active lifetime.

@Override

public void onResume(){

super.onResume();

// Resume any paused UI updates, threads, or processes required

// by the activity but suspended when it was inactive.

}

// Called to save UI state changes at the

// end of the active lifecycle.

@Override

public void onSaveInstanceState(Bundle savedInstanceState) {

// Save UI state changes to the savedInstanceState.

// This bundle will be passed to onCreate if the process is

// killed and restarted.

super.onSaveInstanceState(savedInstanceState);

}

// Called at the end of the active lifetime.

@Override

public void onPause(){

// Suspend UI updates, threads, or CPU intensive processes

// that don’t need to be updated when the Activity isn’t

// the active foreground activity.

super.onPause();

}

// Called at the end of the visible lifetime.

@Override

public void onStop(){

// Suspend remaining UI updates, threads, or processing

// that aren’t required when the Activity isn’t visible.

// Persist all edits or state changes

// as after this call the process is likely to be killed.

super.onStop();

}

// Called at the end of the full lifetime.

@Override

public void onDestroy(){

// Clean up any resources including ending threads,

// closing database connections etc.

super.onDestroy();

}

}

Understanding Activity Lifetimes

Within an Activity’s full lifetime, between creation and destruction, it will go through one or more iterations of the active and visible lifetimes. Each transition will trigger the method handlers described previously.

The Full Lifetime

The full lifetime of your Activity occurs between the first call to onCreate and the final call to onDestroy. It’s possible, in some cases, for an Activity’s process to be terminated without the onDestroy method being called. Use the onCreate method to initialize your Activity: Inflate the user interface, allocate references to class variables, bind data to controls, and create Services and threads. The onCreate method is passed a Bundle object containing the UI state saved in the last call to onSaveInstanceState. You should use this Bundle to restore the user interface to its previous state, either in the onCreate method or by overriding onRestoreInstanceStateMethod. Override onDestroy to clean up any resources created in onCreate, and ensure that all external connections, such as network or database links, are closed. As part of Android’s guidelines for writing efficient code, it’s recommended that you avoid the creation of short-term objects. Rapid creation and destruction of objects forces additional garbage collection, a process that can have a direct impact on the user experience. If your Activity creates the same set of objects regularly, consider creating them in the onCreate method instead, as it’s called only once in the Activity’s lifetime.

The Visible Lifetime

An Activity’s visible lifetimes are bound between calls to onStart and onStop. Between these calls, your Activity will be visible to the user, although it may not have focus and might be partially obscured. Activities are likely to go through several visible lifetimes during their full lifetime, as they move between the foreground and background. While unusual, in extreme cases, the Android run time will kill an Activity during its visible lifetime without a call to onStop. The onStop method should be used to pause or stop animations, threads, timers, Services, or other processes that are used exclusively to update the user interface. There’s little value in consuming resources (such as CPU cycles or network bandwidth) to update the UI when it isn’t visible. Use the onStart (or onRestart) methods to resume or restart these processes when the UI is visible again. The onRestart method is called immediately prior to all but the fi rst call to onStart. Use it to implement special processing that you want done only when the Activity restarts within its full lifetime. The onStart/onStop methods are also used to register and unregister Broadcast Receivers that are being used exclusively to update the user interface. It will not always be necessary to unregister Receivers when the Activity becomes invisible, particularly if they are used to support actions other than updating the UI.

The Active Lifetime

The active lifetime starts with a call to onResume and ends with a corresponding call to onPause. An active Activity is in the foreground and is receiving user input events. Your Activity is likely to go through several active lifetimes before it’s destroyed, as the active lifetime will end when a new Activity is displayed, the device goes to sleep, or the Activity loses focus. Try to keep code in the onPause and onResume methods relatively fast and lightweight to ensure that your application remains responsive when moving in and out of the foreground. Immediately before onPause, a call is made to onSaveInstanceState. This method provides an opportunity to save the Activity’s UI state in a Bundle that will be passed to the onCreate and onRestoreInstanceState methods. Use onSaveInstanceState to save the UI state (such as check button states, user focus, and entered but uncommitted user input) to ensure that the Activity can present the same UI when it next becomes active. During the active lifetime, you can safely assume that onSaveInstanceState and onPause will be called before the process is terminated.

Most Activity implementations will override at least the onPause method to commit unsaved changes, as it marks the point beyond which an Activity may be killed without warning. Depending on your application architecture, you may also choose to suspend threads, processes, or Broadcast Receivers while your Activity is not in the foreground. The onResume method can be very lightweight. You will not need to reload the UI state here as this is handled by the onCreate and onRestoreInstanceState methods when required. Use onResume to re-register any Broadcast Receivers or other processes you may have stopped in onPause.

Creating User Interfaces

It’s vital to create compelling and intuitive User Interfaces for your applications. Ensuring that they are as stylish and easy to use as they are functional should be a primary design consideration.

Basic Android UI Design

Android introduces some new terminology for familiar programming metaphors.

❑ Views Views are the basic User Interface class for visual interface elements (commonly known as controls or widgets). All User Interface controls, and the layout classes, are derived from Views.

❑ ViewGroups View Groups are extensions of the View class that can contain multiple child Views. By extending the ViewGroup class, you can create compound controls that are made up of interconnected child Views. The ViewGroup class is also extended to provide the layout managers, such as LinearLayout, that help you compose User Interfaces.

❑ Activities Activities, represent the window or screen being displayed to the user. Activities are the Android equivalent of a Form. To display a User Interface, you assign a View or layout to an Activity. Android provides several common UI controls, widgets, and layout managers.

For most graphical applications, it’s likely that you’ll need to extend and modify these standard controls, or create composite or entirely new controls to provide your own functionality.

Views

All visual components in Android descend from the View class and are referred to generically as Views. The ViewGroup class is an extension of View designed to contain multiple Views. Generally, View Groups are either used to construct atomic reusable components (widgets) or to manage the layout of child Views. View Groups that perform the latter function are generally referred to as layouts.

Creating Activity User Interfaces with Views

A new Activity starts with a temptingly empty screen onto which you place your User Interface. To set the User Interface, call setContentView, passing in the View instance (typically a layout) to display. Because empty screens aren’t particularly inspiring, you will almost always use setContentView to assign an Activity’s User Interface when overriding its onCreate handler.

The setContentView method accepts either a layout resource ID or a single View instance. This lets you defi ne your User Interface either in code or using the preferred technique of external layout resources. Using layout resources decouples your presentation layer from the application logic, providing the flexibility to change the presentation without changing code. This makes it possible to specify different layouts optimized for different hardware configurations, even changing them at run time based on hardware changes (such as screen orientation).

The following code snippet shows how to set the User Interface for an Activity using an external layout resource. You can get references to the Views used within a layout with the findViewById method. This example assumes that main.xml exists in the project’s res/layout folder.

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

setContentView(R.layout.main);

TextView myTextView = (TextView)findViewById(R.id.myTextView);

}

If you prefer the more traditional approach, you can specify the User Interface in code. The following snippet shows how to assign a new TextView as the User Interface:

@Override

public void onCreate(Bundle icicle) {

super.onCreate(icicle);

TextView myTextView = new TextView(this);

setContentView(myTextView);

myTextView.setText(“Hello, Android”);

}

The setContentView method accepts a single View instance; as a result, you have to group multiple controls to ensure that you can reference a layout using a single View or View Group.

The Android Widget Toolbox

Android supplies a toolbox of standard Views to help you create simple interfaces. By using these controls (and modifying or extending them as necessary), you can simplify your development and provide consistency between applications. Android also supports several more advanced View implementations including date-time pickers, auto-complete input boxes, maps, galleries, and tab sheets. For a more comprehensive list of the available widgets, follow this link

http://code.google.com/android/reference/view-gallery.html.

Layouts

Layout Managers (more generally, “layouts”) are extensions of the ViewGroup class designed to control the position of child controls on a screen. Layouts can be nested, letting you create arbitrarily complex interfaces using a combination of Layout Managers. The Android SDK includes some simple layouts to help you construct your UI. It’s up to you to select the right combination of layouts to make your interface easy to understand and use. The following list includes some of the more versatile layout classes available:

❑ FrameLayout The simplest of the Layout Managers, the Frame Layout simply pins each child view to the top left corner. Adding multiple children stacks each new child on top of the previous, with each new View obscuring the last.

❑ LinearLayout A Linear Layout adds each child View in a straight line, either vertically or horizontally. A vertical layout has one child View per row, while a horizontal layout has a single row of Views. The Linear Layout Manager allows you to specify a “weight” for each child View that controls the relative size of each within the available space.

❑ RelativeLayout Using the Relative Layout, you can define the positions of each of the child Views relative to each other and the screen boundaries.

❑ TableLayout The Table Layout lets you lay out Views using a grid of rows and columns. Tables can span multiple rows and columns, and columns can be set to shrink or grow.

❑ AbsoluteLayout In an Absolute Layout, each child View’s position is defined in absolute coordinates. Using this class, you can guarantee the exact layout of your components, but at a price. Compared to the previous managers, describing a layout in absolute terms means that your layout can’t dynamically adjust for different screen resolutions and orientations.

The Android documentation describes the features and properties of each layout class in detail.

Link: http://code.google.com/android/devel/ui/layout.html.

Using Layouts

The preferred way to implement layouts is in XML using external resources. A layout XML must contain a single root element. This root node can contain as many nested layouts and Views as necessary to construct an arbitrarily complex screen.

The following XML snippet shows a simple layout that places a TextView above an EditText control using a LinearLayout configured to lay out vertically:

<?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”>

<TextView

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:text=”Enter Text Below”

/>

<EditText

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:text=”Text Goes Here!”

/>

</LinearLayout>

Implementing layouts in XML decouples the presentation layer from View and Activity code. It also lets you create hardware-specific variations that are dynamically loaded without requiring code changes. When it’s preferred, or required, you can implement layouts in code. When assigning Views to layouts, it’s important to apply LayoutParameters using the setLayoutParams method, or passing them in to the addView call as shown below:

LinearLayout ll = new LinearLayout(this);

ll.setOrientation(LinearLayout.VERTICAL);

TextView myTextView = new TextView(this);

EditText myEditText = new EditText(this);

myTextView.setText(“Enter Text Below”);

myEditText.setText(“Text Goes Here!”);

int lHeight = LinearLayout.LayoutParams.FILL_PARENT;

int lWidth = LinearLayout.LayoutParams.WRAP_CONTENT;

ll.addView(myTextView, new LinearLayout.LayoutParams(lHeight, lWidth));

ll.addView(myEditText, new LinearLayout.LayoutParams(lHeight, lWidth));

setContentView(ll);

Creating New Views

The ability to extend existing Views, create composite widgets, and create unique new controls lets you create beautiful User Interfaces optimized for your particular workflow. Android lets you subclass the existing widget toolbox and implement your own View controls, giving you total freedom to tailor your User Interface to maximize the user experience

Modifying Existing Views

The toolbox includes a lot of common UI requirements, but the controls are necessarily generic. By customizing these basic Views, you avoid reimplementing existing behavior while still tailoring the User Interface, and functionality, of each control to your application’s needs. To create a new widget based on an existing control, create a new class that extends it — as shown in

the following skeleton code that extends TextView:

import android.content.Context;

import android.util.AttributeSet;

import android.widget.TextView;

public class MyTextView extends TextView {

public MyTextView (Context context, AttributeSet attrs, int defStyle)

{

super(context, attrs, defStyle);

}

public MyTextView (Context context) {

super(context);

}

public MyTextView (Context context, AttributeSet attrs) {

super(context, attrs);

}

}

To override the appearance or behavior of your new View, override and extend the event handlers associated

with the behavior you want to change.

In the following skeleton code, the onDraw method is overridden to modify the View’s appearance, and the onKeyDown handler is overridden to allow custom key press handling:

public class MyTextView extends TextView {

public MyTextView (Context context, AttributeSet ats, int defStyle) {

super(context, ats, defStyle);

}

public MyTextView (Context context) {

super(context);

}

public MyTextView (Context context, AttributeSet attrs) {

super(context, attrs);

}

@Override

public void onDraw(Canvas canvas) {

[ ... Draw things on the canvas under the text ... ]

// Render the text as usual using the TextView base class.

super.onDraw(canvas);

[ ... Draw things on the canvas over the text ... ]

}

@Override

public boolean onKeyDown(int keyCode, KeyEvent keyEvent) {

[ ... Perform some special processing ... ]

[ ... based on a particular key press ... ]

// Use the existing functionality implemented by

// the base class to respond to a key press event.

return super.onKeyDown(keyCode, keyEvent);

}

}