7. Designing Android User Interfaces with Layouts

In this chapter, we discuss how to design user interfaces for Android applications. Here we focus on the various layout widgets you can use to organize screen elements in different ways. These layout objects include LinearLayout, TableLayout, FrameLayout, and RelativeLayout. We also cover some of the more complex View objects we call container views. These are View objects that can contain other View objects and widgets. Finally, we talk about extending application functionality beyond the typical boundaries using AppWidgets.

Creating User Interfaces in Android

Application user interfaces can be simple or complex, involving many different screens or only a few. Layouts and user interface widgets can be defined as application resources or created programmatically at runtime.

Creating Layouts Using XML Resources

As discussed in previous chapters, Android provides a simple way to create layout files in XML as resources provided in the /res/layout project directory. This is the most common and convenient way to build Android user interfaces and is especially useful for defining static screen elements and widget properties that are known in advance, and to set default attributes that can be modified at runtime programmatically.

Caution

If you use Eclipse layout resource designer to design user interfaces for your Android development, it can be helpful to preview the layouts created. This is useful for catching obvious issues, but the preview can’t replicate the exact look and feel of the view the end user sees on the device or phone. For this, you must test your application on the emulator and most important on your target devices.

Almost any ViewGroup or View (or View subclass) attribute can be configured using the XML layout resource files. This method greatly simplifies the user interface design process, moving much of the static creation and layout of user interface widgets, and basic definition of widget attributes, to the XML, instead of littering the code. Developers reserve the ability to alter these layouts programmatically as necessary, but they can set all the defaults in the XML template.

You’ll recognize the following as a simple layout file with a LinearLayout and a single TextView widget. This is the default layout file provided with any new Android project in Eclipse, referred to as /res/layout/main.xml:

image


This block of XML shows a basic layout with a single TextView. The first line, which you might recognize from most XML files, is required. Because it’s common across all the files, we do not show it in any other examples.

Next, we have the LinearLayout element. LinearLayout is a ViewGroup that shows each child View either in a single column or in a single row. When applied to a full screen, it merely means that each child View will be drawn under the previous View if the orientation is set to vertical or to the right of the previous View if orientation is set to horizontal.

Finally, there is a single child View, in this case a TextView. A TextView is a widget, which is also a View. A TextView draws text on the screen. In this case, it draws the text defined in the “@string/hello” string resource.

Creating only an XML file, though, won’t actually draw anything on the screen. A particular layout is usually associated with a particular Activity. In your default Android project, there is only one Activity, which sets the main.xml layout by default. To associate the main.xml layout with the activity, use the method call setContentView() with the identifier of the main.xml layout. The ID of the layout matches the XML filename without the extension. In this case, the preceding example came from main.xml, so the identifier of this layout is simply main.

setContentView(R.layout.main);


Creating Layouts Programmatically

You can also create user interface components at runtime programmatically, but for organization, this is best left for the odd case rather than the norm. The main reason is because the creating of layouts programmatically is onerous and difficult to maintain, whereas the XML resource method is visual and more organized.

The following example shows how to programmatically have an Activity instantiate a LinearLayout view and place two TextView objects within it. No resources whatsoever are used; actions are done at runtime instead.

image


The onCreate() method is called when the Activity is created. The first thing this method does is some normal Activity housekeeping by calling the constructor for the base class.

Next, two TextView widgets are instantiated. The Text property of each TextView is set using the setText() method. All TextView attributes, such as TextSize, are set by making method calls on the TextView object. These actions perform the same function that you have in the past by setting the properties Text and TextSize using the Eclipse layout resource designer, except these properties are set at runtime instead of defined in the layout files compiled into your application package.

Tip

The XML property name is usually similar to the method calls for getting and setting that same widget property programmatically. For instance, android:visibility maps to the method setVisibility() and getVisibility(). In the preceding example TextView, the methods for getting and setting the TextSize property are getTextSize() and setTextSize().

To display the TextView objects appropriately, we need to encapsulate them within a container of some sort (a layout). In this case, we use a LinearLayout with the orientation set to VERTICAL so that the second TextView begins beneath the first, each aligned to the left of the screen. The two TextView widgets are added to the LinearLayout in the order we want them to display.

Finally, we call the setContentView() method, part of your Activity class, to draw the LinearLayout and its contents on the screen.

As you can see, the code can rapidly grow in size as more View widgets are added and more attributes for each View are needed. Here is that same layout, now in an XML layout file called /res/layout/main.xml:

image


You might notice that this isn’t a literal translation of the code example from the previous section, although the output will be identical, as shown in Figure 7.1.

Figure 7.1. Two different methods to create a screen have the same result.

image

First, in the XML layout files, layout_width and layout_height are required attributes. Next, you see that each TextView object has a unique id property assigned so that it can be accessed programmatically at runtime. Finally, the textSize property needs to have its units defined. The XML attribute takes a dimension type (as described in Chapter 5, “Managing Application Resources”) instead of a float.

The end result differs only slightly from the programmatic method. However, it’s far easier to read and maintain. Only one line of code is needed now to display this layout view. Again, that’s

setContentView(R.layout.main);


Organizing Your User Interface with ViewGroups

We talked in the previous chapter about how the class View is the building block for user interfaces in Android. All user interface widgets, such as Button, Spinner and EditText, are derived from the View class.

Now we talk about a special kind of View called a ViewGroup. The classes derived from ViewGroup allow developers to display View objects (including all the user interface widgets you learned about in the previous chapter) on the screen in an organized fashion.

Understanding the Relationship Between View and ViewGroup

Like other View objects, including the widgets talked about in the previous chapter, ViewGroup objects control a rectangle of screen space. What makes ViewGroup different from your typical widget is that ViewGroup objects contain other View objects. A View that contains other View objects is called a parent view. The View objects the parent View contains are called child views, or children.

View child objects are added to a ViewGroup programmatically using the method addView(). In XML, child objects are added to a ViewGroup by defining the child View widget as a child node in the XML (within the parent XML element, as we’ve seen various times using the LinearLayout ViewGroup).

ViewGroup subclasses are broken down into two categories:

• Layout classes

• View container widgets

The Android SDK also provides the Hierarchy Viewer tool to help visualize the layouts you design, as discussed later in this chapter.

Using ViewGroups for Layout

The direct subclasses of ViewGroup are the classes ending with the word “Layout,” for example, LinearLayout, RelativeLayout, and FrameLayout. Each of these layout classes are used to position groups of View objects (widgets) on the screen in different ways. For example, we’ve been using the LinearLayout to arrange various TextView and EditText widgets on the screen in a single vertical column. We could have used an AbsoluteLayout to specify the exact x/y coordinate locations of each widget on the screen instead, but this is not easily portable across many screen resolutions. Users do not generally interact with the Layout objects directly. Instead, they interact with the View objects they contain.

Using ViewGroups as View Containers

The second category of ViewGroup subclasses is the indirect subclasses. These special View objects act as View containers like Layout objects do, but they also provide some kind of functionality that allows users to interact with them like normal widgets. Unfortunately, these classes are not known by any handy names, instead they are named for the kind of functionality they provide.

Some classes that fall into this category include Gallery, GridView, ImageSwitcher, ScrollView, TabHost, and ListView. It can be helpful to consider these objects as different kinds of View browsers. A ListView displays each View as a list item, and the user can browse between the individual View objects using vertical scrolling capability. A Gallery is a horizontal scrolling list of View objects with a center “current” item; the user can browse the View objects in the Gallery by scrolling left and right. A TabHost is a more complex View container, where each Tab can contain a View, and the user chooses the tab by name to see the View contents.

Using the Hierarchy Viewer Tool

In addition to the Eclipse layout resource designer provided with the Android plug-in, the Android Software Development Kit (SDK) provides a user interface tool called the Hierarchy Viewer. The Hierarchy Viewer can be found in the Android SDK subdirectory called /tools.

The Hierarchy Viewer is a visual tool that allows you to inspect your Android application’s View objects and their parent-child relationships. You can drill down on specific View objects and inspect individual View properties at runtime. You can even save screenshots of the current application state on the emulator or the device, although this feature is somewhat unreliable.

To launch the Hierarchy Viewer with your application in the emulator:

  1. Launch your Android application in the emulator.
  2. Navigate to the Android SDK /tools directory and launch the Hierarchy Viewer.
  3. Choose your emulator instance from the Device listing.
  4. Select the application you want to view from the windows available. For example, to load an application from this book, choose one like com.androidbook.parisview.
  5. Click Load View Hierarchy button on the menu bar.

By default, the Hierarchy Viewer loads the Layout View of your application. This includes the parent-child view relationships shown as a Tree View. In addition, a property pane shows the various properties for each View node in the tree when they are selected. A wire-frame model of the View objects on the screen is shown and a red box highlights the currently selected view, which correlates to the same location on the screen.

Tip

You’ll have better luck navigating your application View objects with the Hierarchy Viewer tool if you set your View object id properties to friendly names you can remember instead of the auto-generated sequential id tags provided by default.

Figure 7.2 shows the Hierarchy Viewer loaded with the ParisView project from Chapter 5, which was a one-screen application with a single LinearLayout with a TextView and an ImageView child widget within it. The bulk of the application is shown in the right subtree, starting with LinearLayout with the identifier ParisViewLayout. The other subtree is the Application title bar. A simple double-click on each child node opens that View object individually in its own window.

Figure 7.2. The ParisView Application, shown in the Hierarchy Viewer tool (Layout View).

image

Each View can be separately displayed in its own window by selecting the appropriate View in the tree and choosing the Display View button on the menu bar. In Figure 7.2, you can also see that Display View is enabled on each of the child nodes: the ImageView with the flag, the TextView with the text, as well as the LinearLayout parent node (which includes its children), and lastly the application title bar.

You can use the Pixel Perfect view to closely inspect your application using a loupe (Figure 7.3). You can also load PNG mockup files to overlay your user interface and adjust your application’s look. You can access the Pixel Perfect view by clicking the button with the Nine pixels on it at the bottom left of the Hierarchy Viewer. Click the button with the three boxes depicting the Layout view to return.

Figure 7.3. The ParisView Application, shown in the Hierarchy Viewer tool (Pixel Perfect View).

image

Caution

The Hierarchy Viewer is a useful tool, but it is also somewhat buggy (as you’ll find if you pay any attention to the command window when using it in Windows). To get the Pixel Perfect view to work, we had to click and drag my mouse back and forth across the Normal View (the middle black box) repeatedly until the loupe target crosshairs started working and the application screen loaded. Hopefully, this tool will be improved in future versions of Android. For now, we are of the opinion that a tool that works sometimes is better than a tool that doesn’t work at all.

The Hierarchy Viewer tool is invaluable for debugging drawing issues related to View widgets. If you wonder why something isn’t drawing or if a View is even available, try launching the Hierarchy Viewer and checking that problem View objects’ properties.

You can use the Hierarchy Viewer tool to interact and debug your application user interface. Specifically, developers can use the Invalidate and Request Layout buttons on the menu bar that correspond to View.Invalidate() and View.requestLayout() functions of the UI thread. These functions initiate View objects and draw or redraw them as necessary upon events.

Finally, you can also use the Hierarchy Viewer to deconstruct how other applications (especially sample applications) have handled their layout and displays. This can be helpful if you’d like to re-create a similar layout to another application, especially if it uses stock View types. However, you can also run across View types not provided in the SDK, and you need to implement those custom classes for yourself. For instance, choosing the Launcher window shows us that it uses views such as CellLayout—a view that isn’t exposed in the SDK.

Using Built-In Layout Classes

We talked a lot about the LinearLayout layout, but there are several other types of layouts. Each layout has a different method and order in which it displays child View widgets on the screen. Layouts are derived from android.view.ViewGroup.

The types of layouts built-in to the Android SDK framework include

AbsoluteLayout

FrameLayout

LinearLayout

RelativeLayout

TableLayout

All layouts, regardless of their type, have layout attributes. Layout attributes apply to any child View within that layout. Layout attributes can be set at runtime programmatically, but ideally they are set in the XML layout files using the following syntax:

android:layout_attribute_name="value"


There are several layout attributes that all ViewGroup objects share. These include size attributes and margin attributes. You can find basic layout attributes in the ViewGroup.LayoutParams class. The margin attributes allow each child View within a layout to have padding on each side. Find these attributes in the ViewGroup.MarginLayoutParams classes. There are also a number of ViewGroup attributes for handling child View drawing bounds and animation settings.

Some of the important attributes shared by all ViewGroup subtypes are shown in Table 7.1.

Table 7.1. Important ViewGroup Attributes

image

Here’s an XML layout resource example of a LinearLayout set to the size of the screen, containing one TextView that is set to its full height and the width of the LinearLayout (and therefore the screen).

image


Here is an example of a Button object with some margins set via XML used in a layout resource file.

image


Remember that layout elements can cover any rectangular space on the screen; it doesn’t need to be the entire screen. Because layouts are View objects, layouts can be nested within each other. This allows for great flexibility when developers need to organize screen elements.

A layout such as an AbsoluteLayout might control only a small part of the screen, but that part of the screen will be defined in a pixel-perfect manner. It is also common to start with a FrameLayout or LinearLayout (as you’ve seen in many of the previous chapter examples) as the parent layout for the entire screen and then organize individual screen elements inside the parent layout using whichever layout type is most appropriate.

Now let’s talk about each of the common layout types individually and how they differ from one another.

Tip

You can see an example of each layout in the Android project called SimpleLayout, which is available on the CD or at the book Web site.

Using AbsoluteLayout

An AbsoluteLayout view uses specific x/y coordinates of the child view for laying out each item to an exact location. This might be useful in cases where pixel-perfect layout is required. However, it’s less flexible in that a pixel-specific layout will not adapt well to other device configurations with different screen sizes. It can be helpful when you want to control a specific area of a screen at the pixel level and then include this layout within another larger, more flexible layout on the screen.

Caution

The AbsoluteLayout view class was deprecated in Android 1.5 R1. However, we have chosen to continue to include it in our list of layouts because developers might use this type of layout to port applications from other mobile platforms to Android or other specific purposes. For now, you might still use this class; however, it could disappear at some point in the future. Under most circumstances, other popular layout types such as FrameLayout and RelativeLayout will suffice in place of AbsoluteLayout, so we encourage you to seek other solutions whenever possible.

Figure 7.4 shows an example screenshot with widgets not aligned in any particular way; their coordinates were specifically given.

Figure 7.4. An example of AbsoluteLayout usage.

image

You can find the layout attributes available for AbsoluteLayout child View objects in android.widget.AbsoluteLayout.LayoutParams class. Some of the important attributes specific to AbsoluteLayout child View objects are shown in Table 7.2.

Table 7.2. Important AbsoluteLayout Attributes

image

Here’s an example of an XML layout resource with an AbsoluteLayout and two child View objects, an AnalogClock and a DigitalClock:

image


Using FrameLayout

A FrameLayout view is designed to display a stack of child View items. Multiple views can be added to this layout, but each View is drawn from the top-left corner of the layout. This can be used to show multiple images within the same region, as shown in Figure 7.5, and the layout is sized to the largest child View in the stack.

Figure 7.5. An example of FrameLayout usage.

image

You can find the layout attributes available for FrameLayout child View objects in android.widget.FrameLayout.LayoutParams. Some of the important attributes specific to FrameLayout views are shown in Table 7.3.

Table 7.3. Important FrameLayout View Attributes

image

Here’s an example of an XML layout resource with a FrameLayout and two child View objects, both ImageView objects. The green rectangle is drawn first and the red oval is drawn on top of it. The green rectangle is larger, so it defines the bounds of the FrameLayout:

image


Using LinearLayout

A LinearLayout view organizes its child View objects in a single row, shown in Figure 7.6, or column, depending on whether its orientation attribute is set to horizontal or vertical. This is a very handy layout method for creating forms.

Figure 7.6. An example of LinearLayout (horizontal orientation).

image

You can find the layout attributes available for LinearLayout child View objects in android.widget.LinearLayout.LayoutParams. Some of the important attributes specific to LinearLayout views are shown in Table 7.4.

Table 7.4. Important LinearLayout View Attributes

image

There are many examples of LinearLayout usage in Chapter 5.

Using RelativeLayout

The RelativeLayout view allows you to specify where the child view widgets are in relation to each other. For instance, you can set a child View to be positioned “above” or “below” or “to the left of” or “to the right of” another View, referred to by its unique identifier. You can also align child View objects relative to one another or the parent layout edges. Combining RelativeLayout attributes can simplify creating interesting user interfaces without resorting to multiple layout groups to achieve a desired effect. Figure 7.7 shows how each of the button widgets is relative to each other.

Figure 7.7. An example of RelativeLayout usage.

image

You can find the layout attributes available for RelativeLayout child View objects in android.widget.RelativeLayout.LayoutParams. Some of the important attributes specific to RelativeLayout views are shown in Table 7.5.

Table 7.5. Important RelativeLayout View Attributes

image

image

Here’s an example of an XML layout resource with a RelativeLayout and two child View objects, a Button object aligned relative to its parent, and an ImageView aligned and positioned relative to the Button (and the parent):

image


Using TableLayout

A TableLayout view organizes children into rows, as shown in Figure 7.8. Individual View objects are added within each row of the table using a TableRow layout View (which is basically a horizontally oriented LinearLayout) for each row of the table. Each column of the TableRow can contain one View (or layout with child View objects). View items added to a TableRow are placed in columns in the order they are added. You can specify the column number (zero-based) to skip columns as necessary (bottom row shown in Figure 7.8 demonstrates this); otherwise the View will be put in the next column to the right. Columns scale to the size of the largest View of that column. You can also include normal View objects instead of TableRow elements, if you want the View to take up an entire row.

Figure 7.8. An example of TableLayout usage.

image

You can find the layout attributes available for TableLayout child View objects in android.widget.TableLayout.LayoutParams. You can find all the layout attributes available for TableRow child View objects in android.widget.TableRow.LayoutParams. Some of the important attributes specific to TableLayout View objects are shown in Table 7.6.

Table 7.6. Important TableLayout and TableRow View Attributes

image

Here’s an example of an XML layout resource with a TableLayout with two rows (two TableRow child objects). The TableLayout is set to stretch the columns to the size of the screen width. The first TableRow has three columns; each cell has a Button object. The second TableRow puts only one Button view into the second column explicitly:

image


Using Multiple Layouts on a Screen

Combining different layout methods on a single screen can create complex layouts. Remember that because a layout contains View objects and is, itself, a View, it can contain other layouts. Figure 7.9 demonstrates a combination of layout views used in conjunction to create a more complex and interesting screen.

Figure 7.9. An example of multiple layouts used together.

image

Using Built-In View Container Classes

Layouts are not the only View objects that can contain other View objects and widgets. Although layout views are useful for positioning other View objects on the screen, they aren’t interactive. Now let’s talk about the other kind of ViewGroup: the containers. These View objects often encapsulate other, simpler View types and allow the user some interactive ability to browse the child View objects in a standard fashion.

The types of ViewGroup containers built-in to the Android SDK framework include

• Lists, grids, and galleries with AdapterView

• Switchers with ViewFlipper, ImageSwitcher, TextSwitcher

• Tabs with TabHost and TabWidget

• Dialogs

• Scrolling with ScrollView and HorizontalScrollView

• Hiding and showing content with the SlidingDrawer

Using Data Driven View Containers with AdapterViews

Some of the View container widgets are designed for displaying repetitive View objects in a particular way. Examples of this type of View container widget include ListView, GridView, and GalleryView.

ListView: Contains a vertically scrolling, horizontally filled list of View objects, each of which typically contains a row of data; the user can choose an item to perform some action upon.

GridView: Contains a grid of View objects, with a specific number of columns; this container is often used with image icons; the user can choose an item to perform some action upon.

GalleryView: Contains a horizontally scrolling list of View objects, also often used with image icons; the user can select an item to perform some action upon.

These containers are all types of AdapterView widgets. An AdapterView widget contains a set of child View widgets to display data from some data source. An Adapter generates these child View widgets from a data source. As this is an important part of all these container widgets, we talk about the Adapter objects first.

In this section, you learn how to bind data to View objects using Adapter objects. In the Android SDK, an Adapter reads data from some data source and provides a View object based on some rules, depending on the type of Adapter used. This View is be used to populate the child View objects of a particular AdapterView.

The most common Adapter classes are the CursorAdapter and the ArrayAdapter. The CursorAdapter gathers data from a Cursor whereas the ArrayAdapter gathers data from an array. A CursorAdapter is a good choice to use when using data from a database. The ArrayAdapter is a good choice to use when there is only a single column of data or when the data comes from a resource array.

There are some common elements to know about Adapter objects. When creating an Adapter, you provide a layout identifier. This layout is the template for filling in each row of data. The template you create contains identifiers for particular widgets that the Adapter assigns data to. A simple layout can contain as little as a single TextView widget. When making an Adapter, refer to both the layout resource and the identifier of the TextView widget. The Android SDK provides some common layout resources for use in your application.

Using the ArrayAdapter

An ArrayAdapter binds each element of the array to a single View within the layout resource. Here is an example of creating an ArrayAdapter:

image


In this example, we have a String array called items. This is the array used by the ArrayAdapter as the source data. We also use a layout resource, which is the View that will be repeated for each item in the array. This is defined as follows:

image


This layout resource contains only a single TextView. However, a more complex layout can be used with the constructors that also take the resource identifier of a TextView within the layout. Each child View within the AdapterView that uses this Adapter gets one TextView instance with one of the strings from the String array.

If you have an array resource defined, you can also directly set the entries attribute for an AdapterView to the resource identifier of the array to automatically provide the ArrayAdapter.

Using the CursorAdapter

A CursorAdapter binds one or more columns of data to one or more View objects within the layout resource provided. This is best shown with an example. The following example demonstrates creating a CursorAdapter by querying the Contacts content provider. The CursorAdapter requires the use of a Cursor.

Note

For more information about the Android Cursor object, see Chapter 9, “Using Android Data and Storage APIs.”

image


In this example, we present a couple of new concepts. First, you need to know that the Cursor must contain a field named _id. In this case, we know that the Contacts content provider does have this field. This field will be used later when you handle the user selecting a particular item.

We make a call to managedQuery() to get the Cursor. Then, we instantiate a SimpleCursorAdapter as a ListAdapter. Our layout, R.layout.two_text, has two TextView objects in it, which are used in the last parameter. SimpleCursorAdapter allows us to match up columns in the database with particular widgets in our layout. For each row returned from the query, we will get one instance of the layout within our AdapterView.

Binding Data to the AdapterView

Now that you have an Adapter object, you can apply this to one of the AdapterView widgets. Any of them will work. Although the Gallery technically takes a SpinnerAdapter, the instantiation of SimpleCursorAdapter also returns a SpinnerAdapter. Here is an example of this with a ListView, continuing on from the previous sample code:

((ListView)findViewById(R.id.list)).setAdapter(adapter);


The call to the setAdapter() method of the AdapterView, a ListView in this case, should come after your call to setContentView(). This is all that is required to bind data to your AdapterView. Figure 7.10 shows the same data in a ListView, Gallery, and GridView.

Figure 7.10. ListView, Gallery, and GridView: same data, same list item, different layout views.

image

Handling Selection Events

AdapterView widgets are often used to present data to the user that they will be selecting from. All three of the discussed widgets, ListView, GridView, and Gallery, allow your application to monitor for click events in the same way. You need to call setOnItemClickListener() on your AdapterView and pass in an implementation of the AdapterView.OnItemClickListener class. Here is an example implementation of this class:

image


In the preceding example, av is our AdapterView. The implementation of the onItemClick() method is where all the interesting work happens. The parent parameter is the AdapterView where the item was clicked. This is useful if your screen has more than one AdapterView on it. The View parameter is the specific View within the item that was clicked. The position is the zero-based position within the list of items that the user selected. Finally, the id parameter is the value of the _id column for the particular item that was selected. This is useful for querying for further information about that particular row of data that the item represents.

Your application can also listen for long-click events on particular items. Additionally, selected items can be listened for. Although the parameters are the same, your application receives a call as the highlighted item changes. This can be in response to the user scrolling with the arrow keys and not selecting an item for action.

Using the ListActivity

The ListView widget is commonly used for full-screen menus or lists of items to select from. As such, you might consider using ListActivity as the base class for such screens. Using the ListActivity can simplify these types of screens.

First, to handle item events, you now need to provide an implementation in your ListActivity. For instance, the equivalent of onListItemClickListener is to implement the onListItemClick() method within your ListActivity.

Second, to assign an Adapter, a call to the setListAdapter() method is needed. This is done after the call to the setContentView() method. However, this hints at some of the limitations of using ListActivity.

To use ListActivity, the layout that is set with the setContentView() method must contain a ListView with the identifier set to android:list; this cannot be changed. Second, you can also have a View with an identifier set to android:empty to have a View display when no data is returned from the Adapter. Finally, this works only with ListView widgets, so it has limited use. However, when it does work for your application, it can save on some coding.

Tip

You can create ListView headers and footers using ListView.FixedViewInfo with the ListView methods addHeaderView() and addFooterView().

Organizing Views with TabActivity and TabHost

The Android SDK has a flexible way to provide a tab interface to the user. A screen layout with tabs consists of a TabActivity and a TabHost. The TabHost consists of TabSpecs, a nested class of TabHost, which contains the tab information including the tab title and the contents of the tab. The contents of the tab can either be a predefined View, an Activity launched through an Intent object, or the View can be created with a factory provided by an implementation of TabContentFactory.

Tabs aren’t as complex as they might sound at first. Each tab is effectively a container for a View. That View can come from any View that is ready to be shown, such as an XML layout file. Alternatively, that View can come from launching an Activity. The following example demonstrates each of these methods using View objects and Activity objects created in the previous examples of this chapter:

image


This example creates a tabbed layout view with four tabs on it, as shown in Figure 7.11. The first tab is from the recent GridView sample. The second tab is from the ListView sample before that. The third tab is the basic layout with two TextView objects, fully defined in an XML layout file as previously demonstrated. Finally, the fourth tab is created with a factory.

Figure 7.11. Four tabs displayed.

image

The first action is to get the TabHost instance. This is the object that allows us to add Intent objects and View identifiers for drawing the screen. A TabActivity provides a method to retrieve the current TabHost object.

The next action is only loosely related to tab views. The LayoutInflater is used to turn the XML definition of a View into the actual View objects. This would normally happen when calling setContentView(), but we’re not doing that. The use of the LayoutInflater is required for referencing the View objects by identifier, as is done for the third tab.

Finally, the code adds each of the four tabs to the TabHost in the order that they will be presented. This is accomplished by multiple calls to the addTab() method of TabHost. The first two calls are essentially the same. Each one creates a new Intent with the name of an Activity that launches within the tab. These are the same Activity classes used previously for full-screen display. If the Activity isn’t designed for full screen use, this should work seamlessly.

Next, on the third tab, a layout View is added using its identifier. In the preceding call to the LayoutInflater, the layout file also contains an identifier matching the one used here at the top level of a LinearLayout definition. This is the same one used previously to show a basic LinearLayout example. Again, there was no need to change anything in this view for it to display correctly in a tab.

Next, a tab referencing the content as the TabActivity class is added. This is possible because the class itself also implements TabHost.TabContentFactory, which requires implementing the createTabContent() method. The view will be created the first time the user selects the tab, so no other information is needed here. The tag that creates this tab must be kept track of, though, as that’s how the tabs are identified to the TabHost.

Finally, the method createTabContent() is implemented for use with the fourth tab. The first task here is to check the tag to see if it’s the one kept track of for the fourth tab. When that is confirmed, an instance of the TextView object is created and a text string assigned to it, which contains the current time. The size of the text is set to 24 pixels. The time stamp used in this string can be used to demonstrate when the view is created and that it’s not re-created by simple changing tabs.

The flexibility of tabs that Android provides is great for adding navigation to an application that has a bunch of views already defined. Few changes, if any, need to be made to existing View and Activity objects for them to work within the context of a TabHost.

Exploring Other View Containers

Many other display widgets and objects are available in Android for laying out and designing screens. Some of these are listed here.

Switchers: A ViewSwitcher widget contains only two child View objects and only one of those is shown at a time. It switches between the two, animating as it does so. Primarily, the ImageSwitcher, shown in Figure 7.12, and TextSwitcher objects are used. Each one provides a way to set a new child View, either a Drawable resource or a text string, and then animates from what is displayed to the new contents. Chapter 8, “Drawing and Working with Animation in Android,” discusses more about animation.

Figure 7.12. ImageSwitcher while in the middle of switching between two Drawable resources.

image

Dialogs: A Dialog is not actually a View widget. Instead, it’s like the Activity, but as a pop-up window. A dialog contains a layout in the same way as a full screen and exposes many of the same events, along with some new ones. Other forms of Dialog objects simplify their creation for specific tasks. Figure 7.13 shows an AlertDialog, asking the user to continue.

Figure 7.13. An AlertDialog prompting the user to continue.

image

Scrolling: Although any View widget contains attributes for scrollbars, one of the easiest ways to provide vertical scrolling for a View is with the ScrollView widget. In Android 1.5 R1, a horizontal scrolling mechanism was introduced called HorizontalScrollView. Use either scrolling container as a wrapper, around another View to make scrollbars appear. Additionally, the layout might change, especially sizes of children objects, because the scrolling relaxes the size constraints. Notice, in Figure 7.14, how the background image shows less on the right, but with the scrollbars the user can move around the image.

Figure 7.14. The left screen has no scrollbar, while the right screen has a scrollbar, visually indicating that the user can move around the image.

image

SlidingDrawer: Another View container introduced for developers in Android 1.5 R1 is the SlidingDrawer. This mechanism includes two parts: a handle and a container view. The user drags the handle open and the internal contents are shown; then the user can drag the handle shut and the content disappears. The SlidingDrawer can be used horizontally or vertically and is always used from within a layout representing the larger screen. This makes the SlidingDrawer especially useful for application configurations such as game controls. Users can pull the drawer out, pausing the game, change some features, and then close the SlidingDrawer to resume their game. Figure 7.15 shows how the typical SlidingDrawer looks when pulled open. An example is included in the code on disk.

Figure 7.15. SlidingDrawer sliding open to show contents.

image

Using AppWidgets to Expose Application Views

Introduced officially in Android 1.5 R1, the AppWidget (or widget) provides a new level of application integration with the Android operating system previously not available to mobile developers. AppWidgets act like desktop plug-ins, which are tied back to some underlying Android application and are simply small View objects that have been exposed for use outside the typical boundaries of the application. AppWidgets are typically hosted in container objects such as the HOME screen, which serves as the Android device desktop.

So how might your application use AppWidgets? Well, in any number of ways:

• A travel application might include a simple AppWidget with the current flight security level.

• A picture gallery application might have a simple Picture of the Day AppWidget.

• A To-do application might include a simple AppWidget describing the number of high-priority outstanding items for the day, or the time of the user’s next appointment in the calendar.

Figure 7.16 shows a variety of AppWidgets on the Android Home screen.

Figure 7.16. AppWidgets on the Home screen.

image

Applications that publish widgets are called AppWidget providers (much like applications can be content providers). A component that can contain AppWidgets is called an AppWidget host.

Becoming an AppWidget Provider

Many applications would benefit from becoming AppWidget providers. Although AppWidgets are small in screen size and functionality, they allow the user to benefit from some special functionality from your application without even launching it. They also serve to keep users using your application, by reminding them that they installed it.

Creating an AppWidget for your application involves the following steps:

  1. Declare a BroadcastReceiver in your Android Manifest file, with an Intent filter for the AppWidgetManager.ACTION_APPWIDGET_UPDATE Intent and a <meta-data> tag supplying the XML resource file that describes your AppWidget View.
  2. Create a specially formatted XML resource file for your AppWidget View using the <appwidget-provider> tag and attributes.
  3. Implement the AppWidgetProvider class to handle widget Intent broadcasts, updates, and so on.

Tip

For a complete example of an AppWidget, see the ApiDemos sample application provided with the Android SDK.

Becoming an AppWidget Host

Although somewhat less common, applications might also become AppWidget hosts. AppWidget hosts (android.appwidget.AppWidgetHost) are simply containers that can embed and display AppWidgets. The implementation details of how such containers display different AppWidgets is left up to the developer. However, all AppWidget hosts begin by interacting with the AppWidget service, which serves up the AppWidgets available on the system. For more information on becoming an AppWidget host, see the Android SDK documentation.

Summary

The Android SDK provides a number of powerful methods for designing usable and great-looking screens. This chapter introduced you to many of these. You first learned about many of the Android layout widgets that can control the placement of your widgets on the screen. In many cases, these allow you have a single screen design that works on most screen sizes and aspect ratios.

You then learned about other objects that contain views and how to group or place them on the screen in a particular way. These included such display paradigms as the tab, typically used in a similar way that physical folder tabs are used, in addition to a variety of different widgets for placing data on the screen in a readable and browsable way.

Finally, you also learned how to extend your application by creating simple AppWidgets, which can be hosted by Android system views such as the Home screen.

You now have all the tools you need to develop applications with usable and exciting user interfaces. As you continue your learning with Android, you might want to look through the SDK for other widgets that can be useful to you.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.111.134