8. Positioning with Layouts

In this chapter, we discuss how to design user interfaces for Android applications. Here, we focus on the various layout controls you can use to organize screen elements in different ways. We also cover some of the more complex View controls that we call container views. These are View controls that can contain other View controls.

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 controls can be defined as application resources or created programmatically at runtime.

Although it’s a bit confusing, the term layout is used for two different but related purposes in Android user interface design:

Image In terms of resources, the res/layout/ directory contains XML resource definitions often called layout resource files. These XML files provide a template for how to arrange and draw controls on the screen; layout resource files may contain any number of controls.

Image The term is also used to refer to a set of ViewGroup classes, such as LinearLayout, FrameLayout, TableLayout, RelativeLayout, and GridLayout. These controls are used to organize other View controls. We talk more about these classes later in this chapter.

Creating Layouts Using XML Resources

As discussed in previous chapters, Android provides a simple way to create layout resource files in XML. These resources are stored in res/layout/. This is the most common and convenient way to build Android user interfaces and is especially useful for defining screen elements and default control properties that you know about at compile time. These layout resources are then used much like templates. They are loaded with default attributes that you can modify programmatically at runtime.

You can configure almost any View or ViewGroup subclass attribute 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 controls, and basic definition of control attributes, to the XML instead of littering the code. Developers reserve the ability to alter these layouts programmatically as necessary, but they should set all the defaults in the XML template whenever possible.

You’ll recognize the following as a simple layout file with a RelativeLayout and a single TextView control. Here is the default layout file provided with any new Android project in Android Studio, referred to as res/layout/activity_main.xml, assuming your Activity is named MainActivity:

<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>

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

Next, we have the RelativeLayout element. RelativeLayout is a ViewGroup that shows each child View relative to other views. When applied to a full screen, it merely means that each child View is drawn relative to the specified View.

Finally, there is a single child View—in this case, a TextView. A TextView is a control that 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.

If you create your own XML file, though, that file 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 activity_main.xml layout by default. To associate the activity_main.xml layout with the Activity, use the method call setContentView() with the identifier of the activity_main.xml layout. The ID of the layout matches the XML filename without the extension. In this case, the preceding example came from activity_main.xml, so the identifier of this layout is simply activity_main.xml, and this layout will actually display on the screen as it has been created for us during project creation:

setContentView(R.layout.activity_main);


Image Warning

The Android tools team has made every effort to make the Android Studio Design view layout editor feature complete, and this tool can be helpful for designing and previewing how layout resources will look on a variety of different devices. However, the preview can’t replicate exactly how the layout appears to end users. For this, you must test your application on a properly configured emulator and, more important, on your target devices.


Creating Layouts Programmatically

You can create user interface components such as layouts at runtime programmatically, but for organization and maintainability, it’s best to leave this for the odd case rather than the norm. The main reason is that the creation of layouts programmatically is onerous and difficult to maintain, whereas the XML resources are visual and more organized, and could be used by a separate designer with no Java skills.


Image Tip

The code examples provided in this section are taken from the SameLayout application. The source code for the SameLayout application is provided for download on the book’s website (http://introductiontoandroid.blogspot.com).


The following example shows how to have an Activity instantiate a LinearLayout programmatically and place two TextView controls within it as child controls. The same two string resources are used for the contents of the controls; these actions are done at runtime instead.

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    assert getSupportActionBar() != null;
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    TextView text1 = new TextView(this);
    text1.setText(R.string.string1);

    TextView text2 = new TextView(this);
    text2.setText(R.string.string2);
    text2.setTextSize(TypedValue.COMPLEX_UNIT_SP, 60);

    int pixelDimen = (int) TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP, 16,
        getResources().getDisplayMetrics());

    LinearLayout ll = new LinearLayout(this);
    ll.setOrientation(LinearLayout.VERTICAL);
    ll.setPadding(pixelDimen, pixelDimen,
        pixelDimen, pixelDimen);
    ll.addView(text1);
    ll.addView(text2);

    setContentView(ll);
}

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

Next, the ActionBar is configured and two TextView controls 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 control. These actions perform the same function of setting the properties Text and TextSize as in the Android Studio layout editor, except these properties are set at runtime instead of defined in the layout files compiled into your application package.


Image Tip

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


To display the TextView controls 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 controls are added to the LinearLayout in the order we want them to display.

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

As you can see, the code can rapidly grow in size as you add more View controls and you need more attributes for each View. Here is that same layout, now in an XML layout file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/TextView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/string1" />
    <TextView
        android:id="@+id/TextView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="60sp"
        android:text="@string/string2" />
</LinearLayout>

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

Image

Figure 8.1 Two different methods of creating a screen have the same exact display result seen here.

First, in the XML layout files, layout_width and layout_height are required attributes. Next, you see that each TextView control 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.

The end result differs only slightly from the programmatic method. However, it’s far easier to read and maintain. Now you need only one line of code to display this layout view. Again, the layout resource is stored in the res/layout/resource_based_layout.xml file:

setContentView(R.layout.resource_based_layout);

Organizing Your User Interface

In Chapter 7, “Exploring Building Blocks,” we talked about how the class View is the building block for user interfaces in Android. All user interface controls, such as Button, Spinner, and EditText, derive from the View class.

Now we consider a special kind of View called a ViewGroup. The classes derived from ViewGroup enable developers to display View controls such as TextView and Button controls on the screen in an organized fashion.

It’s important to understand the difference between View and ViewGroup. Like other View controls, including the controls from the previous chapter, ViewGroup controls represent a rectangle of screen space. What makes a ViewGroup different from a typical control is that ViewGroup objects contain other View controls. A View that contains other View controls is called a parent view. The parent View contains View controls called child views, or children.

You add child View controls to a ViewGroup programmatically using the method addView(). In XML, you add child objects to a ViewGroup by defining the child View control 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:

Image Layout classes

Image View container controls

Using ViewGroup Subclasses for Layout Design

Many of the most important subclasses of ViewGroup used for screen design end with “Layout.” For example, the most common layout classes are LinearLayout, RelativeLayout, TableLayout, FrameLayout, and GridLayout. You can use each of these classes to position other View controls on the screen in different ways. For example, we’ve been using the LinearLayout to arrange various TextView and EditText controls on the screen in a single vertical column. Users do not generally interact with the layouts directly. Instead, they interact with the View controls they contain.

Using ViewGroup Subclasses as View Containers

The second category of ViewGroup subclasses is the indirect “subclasses”—some formal and some informal. These special View controls act as View containers like Layout objects do, but they also provide some kind of active functionality that enables users to interact with them like other controls. Unfortunately, these classes are not known by any handy names; instead, they are named for the kind of functionality they provide.

Some of the classes that fall into this category include RecyclerView, GridView, ImageSwitcher, ScrollView, and ListView. It can be helpful to consider these objects as different kinds of View browsers, or container classes. The ListView and RecyclerView display each View control as a list item, and the user can browse the individual controls using vertical scrolling capability.

Using Built-in Layout Classes

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

The types of layouts built into the Android SDK framework include the following:

Image LinearLayout

Image RelativeLayout

Image FrameLayout

Image TableLayout

Image GridLayout


Image Tip

Many of the code examples provided in this section are taken from the SimpleLayout application. The source code for the SimpleLayout application is provided for download on the book’s website.


All layouts, regardless of their type, have basic layout attributes. Layout attributes apply to any child View control within that layout. You can set layout attributes at runtime programmatically, but ideally you set them 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 enable each child View within a layout to have margin padding on each side. Find these attributes in the ViewGroup.MarginLayoutParams class. 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 8.1.

Image

Table 8.1 Important ViewGroup Attributes

Here is an XML layout resource example of a LinearLayout set to the size of the screen with an orientation of vertical so that all child elements will be displayed vertically in a linear fashion, containing one TextView that is set to the full height and width of the LinearLayout (and therefore the screen):

<LinearLayout xmlns:android=
    "http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/TextView01"
        android:layout_height="match_parent"
        android:layout_width="match_parent" />
</LinearLayout>

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

<Button
    android:id="@+id/Button01"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    style="?android:button"
    android:text="Press Me"
    android:layout_marginRight="20dp"
    android:layout_marginTop="60dp" />

Remember that a layout element can cover any rectangular space on the screen; it doesn’t need to fill the entire screen. Layouts can be nested within one another. This provides great flexibility when developers need to organize screen elements. It is common to start with a RelativeLayout, FrameLayout, or LinearLayout 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.

Using LinearLayout

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

Image

Figure 8.2 An example of LinearLayout (horizontal orientation).

You can find the layout attributes available for LinearLayout child View controls in android.widget.LinearLayout.LayoutParams. Table 8.2 describes some of the important attributes specific to LinearLayout views.

Image
Image

Table 8.2 Important LinearLayout View Attributes


Image Note

To learn more about LinearLayout, see the Android API Guides discussion at http://d.android.com/guide/topics/ui/layout/linear.html.



Image Note

The v7 appcompat library provides a LinearLayoutCompat class that provides backward-compatible LinearLayout capabilities found in more recent API versions, making the features available all the way back to API Level 7. You can learn more about the class here: http://d.android.com/reference/android/support/v7/widget/LinearLayoutCompat.html.


Using RelativeLayout

The RelativeLayout view enables you to specify where the child View controls 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 controls relative to one another or to the parent layout edges. Combining RelativeLayout attributes can simplify the creation of interesting user interfaces without resorting to multiple layout groups to achieve a desired effect. Figure 8.3 shows where the Button controls are relative to each other.

Image

Figure 8.3 An example of RelativeLayout usage.

You can find the layout attributes available for RelativeLayout child View controls in android.widget.RelativeLayout.LayoutParams. Table 8.3 describes some of the important attributes specific to RelativeLayout views.

Image
Image
Image

Table 8.3 Important RelativeLayout View Attributes

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

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android=
    "http://schemas.android.com/apk/res/android"
    android:id="@+id/RelativeLayout01"
    android:layout_height="match_parent"
    android:layout_width="match_parent">
    <Button
        android:id="@+id/ButtonCenter"
        android:text="Center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />
    <ImageView
        android:id="@+id/ImageView01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@id/ButtonCenter"
        android:layout_centerHorizontal="true"
        android:src="@drawable/arrow" />
</RelativeLayout>


Image Note

To learn more about RelativeLayout, see the Android API Guides discussion at http://d.android.com/guide/topics/ui/layout/relative.html.


Using FrameLayout

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

Image

Figure 8.4 An example of FrameLayout usage.

You can find the layout attributes available for FrameLayout child View controls in android.widget.FrameLayout.LayoutParams. Table 8.4 describes some of the important attributes specific to FrameLayout views.

Image

Table 8.4 Important FrameLayout View Attributes

Here is an example of an XML layout resource with a FrameLayout and two child View controls, both ImageView controls. 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:

<FrameLayout xmlns:android=
    "http://schemas.android.com/apk/res/android"
    android:id="@+id/FrameLayout01"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center">
    <ImageView
        android:id="@+id/ImageView01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/green_rect"
        android:contentDescription="@string/green_rect"
        android:minHeight="300dp"
        android:minWidth="300dp" />
    <ImageView
        android:id="@+id/ImageView02"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/red_oval"
        android:contentDescription="@string/red_oval"
        android:minHeight="150dp"
        android:minWidth="150dp"
        android:layout_gravity="center" />
</FrameLayout>

Using TableLayout

A TableLayout view organizes children into rows, as shown in Figure 8.5. You add individual View controls 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 controls). You place View items added to a TableRow in columns in the order they are added. You can specify the column number (zero based) to skip columns as necessary (the bottom row shown in Figure 8.5 demonstrates this); otherwise, the View control is 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 controls instead of TableRow elements, if you want the View to take up an entire row.

Image

Figure 8.5 An example of TableLayout usage.

You can find the layout attributes available for TableLayout child View controls in android.widget.TableLayout.LayoutParams. You can find the layout attributes available for TableRow child View controls in android.widget.TableRow.LayoutParams. Table 8.5 describes some of the important attributes specific to TableLayout controls.

Image

Table 8.5 Important TableLayout and TableRow View Attributes

Here is 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 control into the second column explicitly:

<TableLayout xmlns:android=
    "http://schemas.android.com/apk/res/android"
    android:id="@+id/TableLayout01"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    android:stretchColumns="*">
    <TableRow
        android:id="@+id/TableRow01"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/ButtonLeft"
            style="?android:button"
            android:text="Left Door" />
        <Button
            android:id="@+id/ButtonMiddle"
            style="?android:button"
            android:text="Middle Door" />
        <Button
            android:id="@+id/ButtonRight"
            style="?android:button"
            android:text="Right Door" />
    </TableRow>
    <TableRow
        android:id="@+id/TableRow02"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/ButtonBack"
            style="?android:button"
            android:text="Go Back"
            android:layout_column="1" />
    </TableRow>
</TableLayout>

Using GridLayout

Introduced in Android 4.0 (API Level 14), the GridLayout organizes its children inside a grid. But don’t confuse it with GridView; this layout grid is dynamically created. Unlike a TableLayout, child View controls in a GridLayout can span rows and columns, and are flatter and more efficient in terms of layout rendering. In fact, it is the child View controls of a GridLayout that tell the layout where the child views are to be placed. Figure 8.6 shows an example of a GridLayout with five child controls.

Image

Figure 8.6 An example of GridLayout usage.

You can find the layout attributes available for GridLayout child View controls in android.widget.GridLayout.LayoutParams. Table 8.6 describes some of the important attributes specific to GridLayout controls.

Image
Image

Table 8.6 Important GridLayout View Attributes

The following is an example of an XML layout resource with a GridLayout view resulting in four rows and four columns. Each child control occupies a certain number of rows and columns. Because the default span attribute value is 1, we only specify when the element will take up more than one row or column. For instance, the first TextView is one row high and three columns wide. The height and width of each of the View controls are specified to control the look of the result; otherwise, the GridLayout control will automatically assign sizing.

<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:columnCount="4"
    android:rowCount="4" >
    <TextView
        android:layout_width="250dp"
        android:layout_height="100dp"
        android:layout_column="0"
        android:layout_columnSpan="3"
        android:layout_row="0"
        android:background="#f44336"
        android:gravity="center"
        android:text="one" />
    <TextView
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_column="1"
        android:layout_columnSpan="2"
        android:layout_row="1"
        android:layout_rowSpan="2"
        android:background="#ff9800"
        android:gravity="center"
        android:text="two" />
    <TextView
        android:layout_width="150dp"
        android:layout_height="100dp"
        android:layout_column="2"
        android:layout_row="3"
        android:background="#8bc34a"
        android:gravity="center"
        android:text="three" />
    <TextView
        android:layout_width="100dp"
        android:layout_height="150dp"
        android:layout_column="0"
        android:layout_row="1"
        android:background="#673ab7"
        android:gravity="center"
        android:text="four" />
    <TextView
        android:layout_width="100dp"
        android:layout_height="350dp"
        android:layout_column="3"
        android:layout_row="0"
        android:layout_rowSpan="4"
        android:background="#03a9f4"
        android:gravity="center"
        android:text="five" />
</GridLayout>


Image Tip

You may add a GridLayout to legacy applications all the way back to Android 2.1 (API Level 7) using the v7 gridlayout library. To learn more about the support version of this layout, see the following: http://d.android.com/reference/android/support/v7/widget/GridLayout.html.


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 controls and is, itself, a View control, it can contain other layouts.


Image Tip

Want to create a certain amount of space between View controls without using a nested layout? Check out the Space view (android.widget.Space).


Figure 8.7 demonstrates a combination of layout views used in conjunction to create a more complex and interesting screen.

Image

Figure 8.7 An example of multiple layouts used together.


Image Warning

Keep in mind that individual screens of mobile applications should remain sleek and relatively simple. This is not just because this design results in a more positive user experience; cluttering your screens with complex (and deep) View hierarchies can lead to performance problems. Use the Hierarchy Viewer tool to inspect your application layouts; you can also use the lint tool to help optimize your layouts and identify unnecessary components. In addition, you can use <merge> and <include> tags in your layouts for creating a common set of reusable components instead of duplicating them. ViewStub can be used to add more complex views to your layouts during runtime as they are needed, rather than building them directly into your layouts.


Using Container Control Classes

Layouts are not the only controls that can contain other View controls. Although layouts are useful for positioning other View controls on the screen, they aren’t interactive. Now let’s talk about the other kind of ViewGroup: the containers. These View controls encapsulate other, simpler View types and give the user the ability to interactively browse the child View controls in a standard fashion. Much like layouts, each of these controls has a special, well-defined purpose.

Some ViewGroup containers built into the Android SDK framework include

Image Lists and grids

Image ScrollView and HorizontalScrollView for scrolling

Image ViewFlipper, ViewSwitcher, ImageSwitcher, and TextSwitcher for switching


Image Tip

Many of the code examples provided in this chapter are taken from the AdvancedLayouts application. The source code for the AdvancedLayouts application is provided for download on the book’s website.


Using Data-Driven Containers

Some of the View container controls are designed for displaying repetitive View controls in a particular way. Examples of this type of View container control include ListView and GridView:

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

Image GridView: Contains a grid of View controls, with a specific number of columns. This container is often used with image icons; the user can choose an item on which to perform some action.

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

In this section, you learn how to bind data to View controls using Adapter objects. In the Android SDK, an Adapter reads data from some data source and generates the data for a View control based on certain rules, depending on the type of Adapter used. This View is used to populate the child View controls 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 when using data from a database. The ArrayAdapter is a good choice when there is only a single column of data or when the data comes from a resource array.

You should know some common elements of 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 controls to which the Adapter assigns data. A simple layout can contain as little as a single TextView control. When making an Adapter, refer to both the layout resource and the identifier of the TextView control. The Android SDK provides some common layout resources for use in your application.

Using ArrayAdapter

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

private String[] items = {"Item 1", "Item 2", "Item 3" };
ArrayAdapter adapt = new ArrayAdapter<>(this, R.layout.textview, items);

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 is repeated for each item in the array. This is defined as follows:

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="20sp" />

This layout resource contains only a single TextView. However, you can use a more complex layout with 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 provide the ArrayAdapter automatically.

Using CursorAdapter

A CursorAdapter binds one or more columns of data to one or more View controls within the layout resource provided. This is best shown with an example. We also discuss Cursor objects in Chapter 17, “Leveraging Content Providers,” where we provide a more in-depth discussion of content providers.

The following example demonstrates creating a CursorAdapter by querying the Contacts content provider. The CursorAdapter requires the use of a Cursor.

CursorLoader loader = new CursorLoader(
    this, ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
    null, null, null, null);
Cursor contacts = loader.loadInBackground();
ListAdapter adapter = new SimpleCursorAdapter(this,
    R.layout.scratch_layout,
    contacts,
    new String[] {
        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Phone.NUMBER
    }, new int[] {
        R.id.scratch_text1,
        R.id.scratch_text2
    }, 0);

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 ContactsContract content provider does have this field. This field is used later when we handle the user selecting a particular item.


Image Note

The CursorLoader class was introduced in Android 3.0 (API Level 11). If you need to support applications prior to Android 3.0, you can use the Android Support Library to add the CursorLoader class (android.support.v4.content.CursorLoader) to your application. We talk more about the Android Support Library in Chapter 13, “Designing Compatible Applications.”


We instantiate a new CursorLoader to get the Cursor. Then, we instantiate a SimpleCursorAdapter as a ListAdapter. Our layout, R.layout.scratch_layout, has two TextView controls in it, which are used in the last parameter. SimpleCursor-Adapter enables us to match up columns in the database with particular controls in our layout. For each row returned from the query, we 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 controls. Either of them will work. Here is an example of this with a ListView, continuing from the previous sample code:

ListView adapterView = (ListView) findViewById(R.id.scratch_adapter_view)
adapterView.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 8.8 shows the same data in a GridView and ListView.

Image

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

Handling Selection Events

You often use AdapterView controls to present data from which the user should select. Both of the discussed controls—ListView and GridView—enable 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 a sample implementation of this class:

 adapterView.setOnItemClickListener(
      new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent,
            View view, int position, long id) {
        Toast.makeText(ListAdapterSampleActivity.this,
            "Clicked _id=" + id, Toast.LENGTH_SHORT).show();
    }
});

In the preceding example, adapterView 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 selects. Finally, the id parameter is the value of the _id column for the particular item that the user selects. This is useful for querying for further information about the particular row of data that the item represents.

Your application can also listen for long-click events on particular items. Additionally, your application can listen for selected items. 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 ListView with ListFragment

The ListView control is commonly used for full-screen menus or lists of items from which a user selects. Thus, you might consider using ListFragment as the base class for such screens and adding the ListFragment to your View. Using the ListFragment can simplify these types of screens. We discuss Fragments in Chapter 9, “Partitioning with Fragments.”


Image Warning

Before the ListFragment class was added to the Android SDK, the ListActivity class was available for making use of a ListView. Using a ListActivity is no longer recommended; the use of a ListFragment, which provides greater flexibility for your applications, is preferred. The AdvancedLayouts sample application provided with this chapter implements both a ListView using a ListFragment, and a GridView using a Fragment.


First, to handle item events, you now need to provide an implementation in your ListFragment. For instance, the equivalent of OnItemClickListener is to implement the onItemClick() method within your ListFragment that implements the AdapterView.OnItemClickListener interface.

Second, to assign an Adapter, you need a call to the setListAdapter() method. You do this after the call to the setContentView() method of your Activity, in the ListFragment method named onActivityCreated(). However, this hints at some of the limitations of using ListFragment.

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


Image Tip

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


Adding Scrolling Support

One of the easiest ways to provide vertical scrolling for a screen is by using the ScrollView (vertical scrolling) and HorizontalScrollView (horizontal scrolling) controls. Either control can be used as a wrapper container, causing all child View controls to have one continuous scroll bar. The ScrollView and HorizontalScrollView controls can have only one child, though, so it’s customary to have that child be a layout, such as a Linear Layout, which then contains all the “real” child controls to be scrolled through.


Image Tip

The code examples of scrolling in this section are provided in the SimpleScrolling application. The source code for the SimpleScrolling application is available for download on the book’s website.


Figure 8.9 shows a screen with and without a ScrollView control.

Image

Figure 8.9 A screen without (left) and with (right) a ScrollView control.

Exploring Other View Containers

Many other user interface controls are available within the Android Support Library, providing backward-compatible features all the way back to a particular API level noted by its particular Support Library package. Some of the controls you should familiarize yourself with are available in the various support libraries, and a few are listed here:

Image Toolbar: A Toolbar can be used as your application’s ActionBar or may also be used anywhere else in your application’s view hierarchy. If your application has media controls, for example, you may want to embed your media controls inside a Toolbar, and place the Toolbar at the bottom of your application. To use a Toolbar, you need to add the v7 appcompat library to your project.

Image SwipeRefreshLayout: A SwipeRefreshLayout is a useful View container for when your application supports vertical swipe gestures for updating the contents of your application. Your Activity must implement the OnRefreshListener to know how to handle the swipe gesture. To use the SwipeRefreshLayout, you need to add the v4 support library to your project.

Image RecyclerView: A RecyclerView is similar to a ListView container that provides more efficient scrolling when rendering a list that contains a large amount of data to display. To use a RecyclerView, you need to add the v7 recyclerview library to your project. We talk more about RecyclerView in Chapter 12, “Embracing Material Design.”

Image CardView: A CardView is a FrameLayout container that allows you to apply rounded corners and shadows. To use a CardView, you need to add the v7 cardview library to your project. We talk more about CardView in Chapter 12, “Embracing Material Design.”

Image ViewPager: A ViewPager is a useful View container for when your application has many different pages of data and you need to support swiping left and right through that data. To use a ViewPager, you must create a PagerAdapter that provides the data for the ViewPager. Fragments are typically used for paging data with View Pager.

Image DrawerLayout: A layout pattern that has been embraced by the Android team is the DrawerLayout. This layout is especially useful for providing a list of navigation items that are hidden off the screen but presented when users swipe from the left or the right, or when they press the Home button from the action bar if the DrawerLayout resides to the left. DrawerLayout should really be used only for navigation and only when there are more than three top-level views within your application. To use the DrawerLayout, you must add the v4 support library to your project.

Summary

The Android SDK provides a number of powerful methods for designing usable and great-looking screens. This chapter introduced you to many of them. You first learned about some of the Android layout controls that can manage the placement of your controls on the screen. LinearLayout and RelativeLayout are two of the most common, but others such as FrameLayout, GridLayout, and TableLayout provide great flexibility for your layouts. In many cases, these layout controls enable you to 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 a variety of different controls for placing data on the screen in a readable and browsable way. In addition, you learned how to use ListView and GridView as data-driven containers for displaying repetitive content. You now have all the tools you need to develop applications with usable and exciting user interfaces.

Quiz Questions

1. True or false: LinearLayout, FrameLayout, TableLayout, RelativeLayout, and GridLayout refer to a set of ViewControl classes.

2. True or false: A LinearLayout is used for showing each child View either in a single column or in a single row.

3. What is the method name for associating an XML layout resource file with an Activity?

4. True or false: The only way to create an Android user interface is by defining one in a layout resource XML file.

5. What is the syntax for assigning values to attributes within a layout resource XML file?

6. True or false: A FrameLayout is used for wrapping images within a picture frame.

7. What is the name of the control for adding horizontal or vertical scrolling?

8. What are a few View containers available within the Android Support Library?

Exercises

1. Use the Android documentation to determine the difference between a CursorAdapter and a SimpleCursorAdapter, and provide an explanation of that difference.

2. Use the Android documentation to determine the difference between a GridView and a GridLayout, and provide an explanation of that difference.

3. Create a simple Android application demonstrating how to use the ViewSwitcher control. In the ViewSwitcher, define two layouts. The first is a GridLayout defining a login form with a Login button; when the Login button is clicked, switch to a LinearLayout displaying a welcome message. The second layout has a Logout button; when the Logout button is clicked, switch back to the GridLayout.

References and More Information

Android API Guides: “Layouts”:

http://d.android.com/guide/topics/ui/declaring-layout.html

Android SDK Reference regarding the application ViewGroup class:

http://d.android.com/reference/android/view/ViewGroup.html

Android SDK Reference regarding the application LinearLayout class:

http://d.android.com/reference/android/widget/LinearLayout.html

Android SDK Reference regarding the application RelativeLayout class:

http://d.android.com/reference/android/widget/RelativeLayout.html

Android SDK Reference regarding the application FrameLayout class:

http://d.android.com/reference/android/widget/FrameLayout.html

Android SDK Reference regarding the application TableLayout class:

http://d.android.com/reference/android/widget/TableLayout.html

Android SDK Reference regarding the application GridLayout class:

http://d.android.com/reference/android/widget/GridLayout.html

Android SDK Reference regarding the application ListView class:

http://d.android.com/reference/android/widget/ListView.html

Android SDK Reference regarding the application ListActivity class:

http://d.android.com/reference/android/app/ListActivity.html

Android SDK Reference regarding the application ListFragment class:

http://developer.android.com/reference/android/app/ListFragment.html

Android SDK Reference regarding the application GridView class:

http://d.android.com/reference/android/widget/GridView.html

Android SDK Reference regarding the application Toolbar class:

http://d.android.com/reference/android/support/v7/widget/Toolbar.html

Android SDK Reference regarding the application SwipeRefreshLayout class:

http://d.android.com/reference/android/support/v4/widget/SwipeRefreshLayout.html

Android SDK Reference regarding the application RecyclerView class:

http://d.android.com/reference/android/support/v7/widget/RecyclerView.html

Android SDK Reference regarding the application CardView class:

http://developer.android.com/reference/android/support/v7/widget/CardView.html

Android SDK Reference regarding the application ViewPager class:

http://d.android.com/reference/android/support/v4/view/ViewPager.html

Android SDK Reference regarding the application PagerAdapter class:

http://d.android.com/reference/android/support/v4/view/PagerAdapter.html

Android SDK Reference regarding the application DrawerLayout class:

http://d.android.com/reference/android/support/v4/widget/DrawerLayout.html

Android Tools: “Support Library”:

http://d.android.com/tools/support-library/index.html

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

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