Android UI Fundamentals: Working with Basic and Advanced Views
- Creating a Basic Form
- Displaying Images
- Creating Maps and Displaying Websites
- Wrapping Up
The most basic element of Android user interfaces is the View class. A view represents an area of the screen. Buttons, lists, webpages, and even empty spaces are represented by views. Android contains a rich array of pre-built View classes that provide much of the functionality you will need. When the built-in views aren’t enough, it’s possible to create special views that are just right for your application. In this chapter, you will learn about the basic view types you can use to build your layout, discover how to load and display images, and explore the more advanced views available in Android: MapView and WebView.
Creating a Basic Form
The TimeTracker app looks pretty good so far, but it’s time to add more than just a list of times. In this chapter, you’ll add some text entry forms and split the app into multiple activities. When you’re finished, you’ll have something that looks like Figure 4.1. This section will cover the basic widgets you see in the image, as well as how to arrange them.
Figure 4.1 The TimeTracker app will have task detail and task edit screens.
TextView and EditText
The most basic view available on Android is the TextView, which allocates an area of the screen to display text. You will use this view a lot in your layouts. An EditText is a TextView that is configured to allow the user to edit the text inside it (Figure 4.2). Tapping an EditText will display a cursor and the device software keyboard, allowing the user to enter new text or edit the existing text. The TextView has optional attributes such as size, font, and color that allow you to change the appearance of the text.
Figure 4.2 A TextView and an EditText
Creating the TextView
To create the new UI for the TimeTracker app, you’ll need to create two new layouts: task_detail.xml and edit_task.xml. They will look very similar, but edit_task.xml will use EditText instead of TextView. Here is the XML for task_detail.xml:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/counter"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="@string/sample_time"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="50sp" >
</TextView>
<Button
android:id="@+id/start_stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:text="@string/start" />
<TextView
android:id="@+id/task_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:text="@string/task_name"
android:textSize="20dp" >
</TextView>
<TextView
android:id="@+id/task_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:text="@string/date_select"
android:textSize="20dp" />
<TextView
android:id="@+id/task_desc"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="20dp"
android:layout_weight="1"
android:text="@string/description"
android:textSize="20dp" />
</LinearLayout>
This XML layout keeps the counter and the Start/Stop button from Chapter 2, but the task list is replaced with the new task detail fields. Note the use of layout_weight on the description to fill the entire display.
Simplifying text entry
In addition to general text entry, you will probably want your users to enter textual data in a particular format. Data such as email addresses, phone numbers, and passwords are particularly common on a mobile device. With a hardware keyboard, the user just enters data normally, but because Android devices have a software keyboard, the keys can be changed to make entry of certain data types easier. For example, if you have a field that accepts only numerical data, the keyboard will display just the number pad.
The inputType attribute of your EditText class is a simple bit mask that defines the type of data you expect the user to enter. The system can then display an appropriate keyboard type. You can combine EditText flags (attributes) so that the system creates a targeted input keyboard. For example, the following EditText attributes will make the keyboard a number pad for easy entry of phone numbers (Figure 4.3):
<EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="phone" />
Figure 4.3 The keyboard displayed when the inputType of an EditText is set to phone
Along with changing the keyboard, you can use inputType to change the behavior of the EditText; for example, use flags like textCapSentences and textAutoCorrect to add capitalization and autocorrection to what the user types. In addition to configuring the input options, you can use an IME option to set the text for the Enter button, which appears in the lower-right corner of the stock Android keyboard: Use the imeOptions attribute to select actionGo, actionSearch, actionSend, actionNext, or actionDone to give the user a visual indication of what action will be taken when they are finished entering text.
Now you can create the content of the edit_task.xml layout. Create the file, and add the following XML:
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > EditText android:id="@+id/task_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/task_name" android:layout_margin="10dp" android:textSize="24dp" > </EditText> <EditText android:id="@+id/description" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:layout_margin="10dp" android:hint="@string/description" android:gravity="top|left" /> <DatePicker android:id="@+id/datePicker1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:calendarViewShown="false" android:layout_margin="10dp" /> </LinearLayout>
Here you’re using the android:hint attribute rather than android:text. This displays the desired preset text but removes it as soon as the user starts typing a value into the field. This edit_task.xml layout also uses the DatePicker view to make date entry easier.
Buttons
You’ve already used buttons to build the current TimeTracker UI. Buttons are simply TextViews that have a special background image—this background is actually an XML file that lists the images that should be used for the different button states (normal, hovered, focused, and pressed). This type of XML resource is called a state list resource, and you’ll learn more about creating it later in this chapter.
- Add a Finished button to the edit_task.xml layout:
<Button android:id="@+id/finished" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/finished" > </Button>
- Add an Edit button to the task_list.xml layout:
<Button android:id="@+id/edit" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/edit" > </Button>
Boolean Buttons
Buttons are convenient for indicating on/off states. Android has a number of views, including toggle buttons, checkboxes, and radio buttons, that subclass the Button class and present a toggle between a true value and a false value. In addition, Android 4.0 introduced an option called the switch.Figure 4.4 shows all these options for the 4.0 release of Android.
Figure 4.4 Boolean buttons on Android 4.0
Spinners
A spinner looks like a button and displays a list of choices when pressed.Figure 4.5 shows an example of a spinner choice list. The options presented by a spinner can be specified using the XML android:entries attribute, or you can use a data adapter to load entries programmatically (you’ll learn more about loading entries into views via data adapters in Chapter 6).
Figure 4.5 A spinner on Android 4.0
ScrollView
Adding entry fields to a form is simple, but what happens if you cannot fit all the views on one screen? In these cases, it’s often useful to allow scrolling in order to fit more elements in a single activity. To achieve this effect, you need to wrap your views in a ScrollView container. A ScrollView allows you to create a view that is larger than the physical screen on a device and scroll it to reveal the full contents. ScrollView is actually a subclass of FrameLayout, but it adds the ability to scroll its content. You typically place another layout container inside the ScrollView to arrange the child views.
Since you want the user to enter an arbitrary amount of description text in the time tracker, you’ll want to use a ScrollView so they can see it all. Wrap the existing LinearLayout contents in a ScrollView:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true" >
<LinearLayout> <!-- Rest of code here --> </LinearLayout></ScrollView>
This code should be self-explanatory by now. The ScrollView simply wraps the LinearLayout, which contains the text and buttons you have already created. Notice the android:fillViewPort attribute? This prevents some odd behavior, which you’ll learn about next.
The Fillviewport Attribute
A common issue you may experience with ScrollView is its interaction with child views that are smaller than the display. When the child view is larger than the display, the ScrollView behaves as expected, allowing you to scroll to see the full view. However, when the child view is smaller than the display, the ScrollView will automatically shrink itself to match the size of its content. The proper way to handle this is to use the fillViewPort attribute, which will cause the child views of a ScrollView to expand to the size of the display, if necessary; if they are already larger than the display, nothing happens. A simple example will demonstrate.
A frequent task is displaying a block of text with a button at the bottom (such as in a license agreement to which a user must agree). Figure 4.6 shows the desired result: a long block of text that scrolls to reveal a button. When the text is smaller than a single screen, the naive implementation of ScrollView results in Figure 4.7—the button should still be pinned to the bottom of the screen but is instead directly below the text. The ScrollView only takes up as much space as its content. To fix this, set the fillViewPort attribute to true. Here is the code to correctly implement scrolling for any size of text, resulting in Figure 4.8.
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:fillViewport="true" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="1.0" android:text="@string/hello" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Button" /> </LinearLayout> </ScrollView>
Figure 4.6 The desired ScrollView result, with a long block of text scrolling to reveal a button
Figure 4.7 The ScrollView result if the fillViewPort attribute is not set to true
Figure 4.8 Because the android:fillViewPort attribute was used, the button is now correctly pinned to the bottom of the screen.
Try using ScrollView with and without the fillViewPort attribute to see how its behavior changes.