The Views in the following section are the meat and potatoes of your application; essential widgets that you'll use over and over and that your users will be familiar with from other applications.
11.1.1. TextView and EditText
A TextView, as shown in the line "This is some text" in Figure 11-1, is just what you'd expect: a place to display a text string. The vanilla TextView is for display only, whereas EditText is a predefined subclass of TextView that includes rich editing capabilities.
Figure 11-1. TextView, EditText, and Button
Each TextView has the attributes you'd expect of such a component: you can change its height, width, font, text color, background color, and so forth. TextViews also have some useful unique attributes:
autoLink
autoText
editable
inputMethod
Example 11-1 shows how to use a TextView and an EditText with Buttons. (Buttons are covered in the next section.) It also shows the XML layout file (main.xml), which uses pretty standard and recommended layout parameters.
Example 11-1. Layout file for TextView and EditView example
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/txtDemo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<EditText
android:id="@+id/eTxtDemo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/btnDone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Log it"
/>
</LinearLayout>
|
Example 11-2 contains the accompanying Java source (TextViewDemo.java).
Example 11-2. Java for TextView and EditView: TextViewDemo.java
package com.oreilly.demo;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class TextViewDemo extends Activity {
private static TextView txt1;
private static EditText etxt1;
private static Button btn1;
// Create a button click listener for the Done button.
private final Button.OnClickListener btnDoneOnClick = new Button.OnClickListener() {
public void onClick(View v) {
String input = etxt1.getText().toString();
//Log the input string
Log.v("TextViewDemo", input);
etxt1.setText("");
}
};
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Get pointers to the Views defined in main.xml
txt1 = (TextView) findViewById(R.id.txtDemo);
etxt1 = (EditText) findViewById(R.id.eTxtDemo);
btn1 = (Button) findViewById(R.id.btnDone);
//Set the string displayed in TextView1
txt1.setText("This is some text.");
//Set the OnClickListener for the Done button
btn1.setOnClickListener(btnDoneOnClick);
}
} |
Here are some of the highlights of the code:
Now the user can enter and edit text in the EditText, and when he clicks on "Log it", the OnClickListener is called and the text is written to the logcat log. The string in the EditText is cleared out, and the widget is ready for another entry.
11.1.2. Button and ImageButton
The Button View is just a button, printed with some text to identify it, that the user can click to invoke some action. The previous section created a Button and connected it to an OnClickListener method that executes when the Button is clicked.
Android has a very visual, mobile-oriented user interface, so you might want to use a button with an image on it rather than one with text. Android provides the ImageButton View for just that purpose. You can adapt Example 11-2 to use an ImageButton by making one change in the XML file and another in the Java code:
- In main.xml, replace the Button definition for btnDone with an ImageButton:
... <ImageButton android:id="@+id/btnDone" android:layout_width="wrap_content" android:layout_height="wrap_content" /> ...
- In TextViewDemo.java, redefine btn1 as an ImageButton and add a line to set the image to a PNG image in the drawable directory:
... private static ImageButton btn1; ... /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //Get pointers to the Views defined in main.xml txt1 = (TextView) findViewById(R.id.txtDemo); etxt1 = (EditText) findViewById(R.id.eTxtDemo); btn1 = (ImageButton) findViewById(R.id.btnDone); ... //Set the image for the Done button btn1.setImageResource(R.drawable.log); ...
The button now appears as shown in Figure 11-2.
Figure 11-2. Text boxes with an ImageButton
11.1.3. Adapters and AdapterViews
Adapters and AdapterViews are an important and useful basis for several of the views discussed in the rest of this chapter. Using extensions to these classes, you can address an extremely wide variety of situations.
The AdapterView is a generic, list-oriented view of data. Any collection of data objects that can be ordered in some relatively stable way can be displayed through an AdapterView. An AdapterView is always associated with an Adapter, which acts as the bridge between it and the underlying data collection. The Adapter has two responsibilities:
- At the request of the AdapterView, the Adapter must be able to find the data object that corresponds to a particular index. It must, in other words, be able to find the data object that is visible in the AdapterView at a particular location.
- Inversely, the Adapter must be able to supply a view through which the data at a particular index can be displayed.
It takes only a moment's reflection to understand how the AdapterView works: It is a ViewGroup that contains all the machinery necessary to serve as both the View and Controller for a collection of generic widgets. It can lay them out on the display, pass in clicks and keystrokes, and so on. It need never concern itself with what the subviews actually display; it distinguishes them only by their indexes. Whenever it needs to perform either of the two operations that are not entirely generic—creating a new view or getting the data object attached to a particular view—it relies on the Adapter to convert an index into either a data object or the view of a data object.
The AdapterView requests new views from an implementation of the Adapter interface, as it needs them, for display. For instance, as a user scrolls though a list of contacts, the AdapterView requests a new view for each new contact that becomes visible. As an optimization, the AdapterView may offer a view that is no longer visible (in this case, one that has scrolled off the display) for reuse. This can dramatically reduce memory churn and speed up display.
When offered a recycled view, however, the Adapter must verify that it is the right kind of view through which to display the data object at the requested index. This is necessary because the Adapter is not limited to returning instances of a single view class in response to the request for a view. If the Adapter represents several kinds of objects, it might create several different types of views, each applicable to some subset of the data objects in the collection. A list of contacts, for instance, might have two entirely different view classes: one for displaying acquaintances that are currently online and another for those who are not. The latter might completely ignore clicks, whereas the former would open a new chat session when clicked.
Although AdapterView and Adapter are both abstract and cannot be directly instantiated, the UI toolkit includes several prebuilt Adapters and AdapterViews that can be used unmodified or further subclassed to provide your own customizations. ListAdapter and SpinnerAdapter are particularly useful Adapters, while ListView, GridView, Spinner, and Gallery are all handy subclasses of AdapterView. If you plan to create your own subclass of AdapterView, a quick look at the code for one of these classes will get you off to a running start.
A good example of the use of an AdapterView can be found in Section 11.2.1. The Gallery view in that section is a subclass of AdapterView, and uses a subclass of Adapter called ImageAdapter.
11.1.4. CheckBoxes, RadioButtons, and Spinners
The Views we present in this section are probably familiar to you from other user interfaces. Their purpose is to allow the user to choose from multiple options. CheckBoxes are typically used when you want to offer multiple selections with a yes/no or true/false choice for each. RadioButtons are used when only one choice is allowed at a time.
Spinners are similar to combo boxes in some frameworks. A combo box typically displays the currently selected option, along with a pull-down list from which the user can click on another option to select it.
Android has adapted these familiar components to make them more useful in a touchscreen environment. Figure 11-3 shows the three types of multiple-choice Views laid out on an Android application, with the Spinner pulled down to show the options.
Figure 11-3. CheckBox, RadioButtons, and Spinner
The layout XML file that created the screen in the figure looks like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<CheckBox
android:id="@+id/cbxBox1"
android:layout_width="20dp"
android:layout_height="20dp"
android:checked="false"
/>
<TextView
android:id="@+id/txtCheckBox"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="CheckBox: Not checked"
/>
<RadioGroup
android:id="@+id/rgGroup1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RadioButton android:id="@+id/RB1" android:text="Button1" />
<RadioButton android:id="@+id/RB2" android:text="Button2" />
<RadioButton android:id="@+id/RB3" android:text="Button3" />
</RadioGroup>
<TextView
android:id="@+id/txtRadio"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="RadioGroup: Nothing picked"
/>
<Spinner
android:id="@+id/spnMusketeers"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="2dp"
/>
</LinearLayout>
The file just lists each View we want on the screen along with the attributes we want. A RadioGroup is really a ViewGroup, so it contains the appropriate RadioButton Views. Example 11-3 shows the Java file that responds to user clicks.
Example 11-3. Java for CheckBox, RadioButtons, and Spinner
package com.oreilly.select;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.google.android.maps.GeoPoint;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.AdapterView.OnItemSelectedListener;
public class SelectExample extends Activity {
private CheckBox checkBox;
private TextView txtCheckBox, txtRadio;
private RadioButton rb1, rb2, rb3;
private Spinner spnMusketeers;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
checkBox = (CheckBox) findViewById(R.id.cbxBox1);
txtCheckBox = (TextView) findViewById(R.id.txtCheckBox);
txtRadio = (TextView) findViewById(R.id.txtRadio);
rb1 = (RadioButton) findViewById(R.id.RB1);
rb2 = (RadioButton) findViewById(R.id.RB2);
rb3 = (RadioButton) findViewById(R.id.RB3);
spnMusketeers = (Spinner) findViewById(R.id.spnMusketeers);
// React to events from the CheckBox
checkBox.setOnClickListener(new CheckBox.OnClickListener() {
public void onClick(View v){
if (checkBox.isChecked()) {
txtCheckBox.setText("CheckBox: Box is checked");
}
else
{
txtCheckBox.setText("CheckBox: Not checked");
}
}
});
// React to events from the RadioGroup
rb1.setOnClickListener(new RadioGroup.OnClickListener() {
public void onClick(View v){
txtRadio.setText("Radio: Button 1 picked");
}
});
rb2.setOnClickListener(new RadioGroup.OnClickListener() {
public void onClick(View v){
txtRadio.setText("Radio: Button 2 picked");
}
});
rb3.setOnClickListener(new RadioGroup.OnClickListener() {
public void onClick(View v){
txtRadio.setText("Radio: Button 3 picked");
}
});
// Set up the Spinner entries
List<String> lsMusketeers = new ArrayList<String>();
lsMusketeers.add("Athos");
lsMusketeers.add("Porthos");
lsMusketeers.add("Aramis");
ArrayAdapter<String> aspnMusketeers =
new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,
lsMusketeers);
aspnMusketeers.setDropDownViewResource
(android.R.layout.simple_spinner_dropdown_item);
spnMusketeers.setAdapter(aspnMusketeers);
// Set up a callback for the spinner
spnMusketeers.setOnItemSelectedListener(
new OnItemSelectedListener() {
public void onNothingSelected(AdapterView<?> arg0) { }
public void onItemSelected(AdapterView<?> parent, View v,
int position, long id) {
// Code that does something when the Spinner value changes
}
});
}
} |
The Views work as follows:
CheckBox
RadioGroup- As mentioned earlier, the RadioGroup View is really a ViewGroup that contains any number of RadioButton Views. The user can select only one of the buttons at a time, and you capture the selections by setting OnClickListeners for each RadioButton. Note that clicking on one of the RadioButtons does not fire a click event for the RadioGroup.
Spinner- Spinners require the most work of these three Views, but can also provide the best use of scarce screen real estate. As shown, the Spinner is normally collapsed to the currently selected entry, and when you touch the down arrow on the right, it presents a drop-down list of the other choices. To make that happen, you must:
- Create a list of the selections (which can be a dynamic list built and changed by your application).
- Create an ArrayAdapter from the list that the Spinner can use for its drop-down list. Note that the formats shown for the ArrayAdapter (simple_spinner_item and simple_spinner_dropdown_item) are defined by Android; they do not appear in your resource XML files.
- Create an onItemSelectedListener for the Spinner to capture select events. The listener has to contain both an onItemSelected method and an onNothingSelected method.
11.2. ViewGroups
ViewGroups are Views that contain child Views. Each ViewGroup class embodies a different set of assumptions about how to display its child Views. All ViewGroups descend from the android.view.ViewGroup class. Layouts, which we'll discuss later in the chapter, are a subset of ViewGroups.11.2.1. Gallery and GridView
The Gallery ViewGroup (Figure 11-4) displays multiple items in a horizontally scrolling list. The currently selected item is locked in the center of the screen. Any items that approach the edge of the screen begin to fade, giving the user the impression that there may be more items "around the corner." The user can scroll horizontally through the items within the gallery. This ViewGroup is useful when you want to present a large set of possible choices to the user without using too much screen real estate.
Figure 11-4. The Gallery ViewGroup
A GridView (Figure 11-5, shown later) is very similar to a Gallery. Like a Gallery, the GridView displays many child Views that the user can manipulate. But in contrast to a Gallery, which is a one-dimensional list that the user can scroll horizontally, a GridView is a two-dimensional array that the user can scroll vertically.The Gallery and GridView classes both descend from the AdapterView class, so you need a subclass of Adapter to provide a standardized way to access the underlying data. Any class that implements the Adapter class must implement the following abstract functions from that class:
int getCount
Object getItem(int position)
long getItem(int position)
View getView(int position, View convertView, ViewGroup parent)
The ApiDemos application's views.Gallery1.java file shows off the Gallery ViewGroup nicely. The demo displays a variety of images for the user to select, and when the user does select one, the image's index number briefly appears as toast.The ApiDemos application also includes two example GridView Activities that show how to use the GridView. We will not examine the GridView here, because the Gallery example is so similar.Example 11-4 shows how to use a Gallery ViewGroup. Example 11-4 shows the XML layout file (gallery_1.xml).Example 11-4. Layout file for Gallery example
<?xml version="1.0" encoding="utf-8"?> <Gallery xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/gallery" android:layout_width="fill_parent" android:layout_height="wrap_content" />Here are some of the highlights of the layout code:Figure 11-5. The GridView ViewGroup
Now we'll turn our attention to the Java implementation, Gallery1.java, shown in Example 11-5. We've modified the code from ApiDemos slightly to remove some features that do not add to our understanding of the Gallery ViewGroup.Example 11-5. Java for Gallery: Gallery1.java
public class Gallery1 extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.gallery_1); // Reference the Gallery view Gallery g = (Gallery) findViewById(R.id.gallery); // Set the adapter to our custom adapter (below) g.setAdapter(new ImageAdapter(this)); // Set a item click listener, and just Toast the clicked position g.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { Toast.makeText(Gallery1.this, "" + position, Toast.LENGTH_SHORT).show(); } }); }Here are some of the highlights of the code:In Example 11-5, the setAdapter function tells the Gallery object to use the ImageAdapter object as its Adapter. Example 11-6 defines our ImageAdapter class. This ImageAdapter implements all of the abstract functions required in its base class, BaseAdapter. For the simple case of this demo, picture resources represent the data that the Gallery view is displaying. An integer array, mImageIds, contains the resource IDs of the picture resources.Example 11-6. Java for Gallery's Adapter
public class ImageAdapter extends BaseAdapter { int mGalleryItemBackground; private Context mContext; private Integer[] mImageIds = { R.drawable.gallery_photo_1, R.drawable.gallery_photo_2, R.drawable.gallery_photo_3, R.drawable.gallery_photo_4, R.drawable.gallery_photo_5, R.drawable.gallery_photo_6, R.drawable.gallery_photo_7, R.drawable.gallery_photo_8 }; public ImageAdapter(Context c) { mContext = c; TypedArray a = obtainStyledAttributes(android.R.styleable.Theme); mGalleryItemBackground = a.getResourceId( android.R.styleable.Theme_galleryItemBackground, 0); a.recycle(); } public int getCount() { return mImageIds.length; } public Object getItem(int position) { return position; } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ImageView i = new ImageView(mContext); i.setImageResource(mImageIds[position]); i.setScaleType(ImageView.ScaleType.FIT_XY); i.setLayoutParams(new Gallery.LayoutParams(136, 88)); // The preferred Gallery item background i.setBackgroundResource(mGalleryItemBackground); return i; } } }Here are some of the highlights of the code:11.2.2. ListView and ListActivity
ListView is similar to Gallery, but uses a vertically scrolling list in place of Gallery's horizontally scrolling list. To create a ListView that takes up the entire screen, Android provides the ListActivity class (Figure 11-6).The ApiDemos application includes many examples of ListActivity. The simplest is the List1 class, which displays a huge number of cheese names in a list. The cheese names are kept in a simple String array (who knew there were that many cheese varieties!):public class List1 extends ListActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Use an existing ListAdapter that will map an array // of strings to TextViews setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mStrings)); } private String[] mStrings = { "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale", ...Figure 11-6. ListActivity
Filling the ListView in the ListActivity is a simple matter of calling setListAdapter and passing it an ArrayAdapter that contains a reference to the list of strings.11.2.3. ScrollView
A ScrollView is a container for another View that lets the user scroll that View vertically (a scrollbar is optional). A ScrollView often contains a LinearLayout, which in turn contains the Views that make up the form.Don't confuse ScrollView with ListView. Both Views present the user with a scrollable set of Views, but the ListView is designed to display a set of similar things, such as the cheeses in the previous section. The ScrollView, on the other hand, allows an arbitrary View to scroll vertically. The Android documentation warns that one should never house a ListView within a ScrollView, because that defeats the performance optimizations of a ListView.A ScrollView is a FrameLayout, which means that it can have only one child View. The most popular View for this purpose is a LinearLayout.The following layout code from ApiDemos, scroll_view_2.xml, shows how to set up a ScrollView. The XML layout resource is sufficient; this example includes no extra Java code:<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:scrollbars="none"> <LinearLayout android:id="@+id/layout" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/scroll_view_2_text_1"/> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/scroll_view_2_button_1"/> </LinearLayout> </ScrollView>Here are some of the highlights of the code:Figure 11-7. The first tab of a TabHost ViewGroup
Figure 11-8. The second tab of a TabHost ViewGroup
11.2.4. TabHost
Most modern UIs provide an interface element that lets the user flip through many pages of information quickly using tabs, with each "screen" of information available when its tab is pressed. Android's option is the TabHost View. Figures Figure 11-7 through Figure 11-10 show how it operates.Figure 11-9. The third tab of a TabHost ViewGroup
Figure 11-10. The fourth tab of a TabHost ViewGroup
Android enables the developer to choose between three different approaches for setting the tab's content. The developer can:- Set the content of a tab to an Intent. Figures Figure 11-7 and Figure 11-9 use this method.
- Use a TabContentFactory to create the tab's content on-the-fly. Figure 11-8 uses this method.
- Retrieve the content from an XML layout file, much like that of a regular Activity. Figure 11-10 uses this method.
We'll examine each of these possibilities using a modified Activity from the ApiDemos application. The fourth tab is not part of the ApiDemos, but combines some other TabHost demonstration Activities in ApiDemos.Let's start by looking at the tabs4.xml layout file (Example 11-7).Example 11-7. Layout file for TabHost (tabs4.xml)
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/view4" android:background="@drawable/green" android:layout_width="fill_parent" android:layout_height="fill_parent" android:text="@string/tabs_4_tab_4"/> </FrameLayout>Here are some of the highlights of the code:And now we'll dissect the Java code that produces the tabs (Example 11-8).Example 11-8. Java for TabHost
public class Tabs4 extends TabActivity implements TabHost.TabContentFactory { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final TabHost tabHost = getTabHost(); LayoutInflater.from(this).inflate(R.layout.tabs4, tabHost.getTabContentView(), true); tabHost.addTab(tabHost.newTabSpec("tab1") .setIndicator("intent") .setContent(new Intent(this, List1.class))); tabHost.addTab(tabHost.newTabSpec("tab2") .setIndicator("factory", getResources().getDrawable(R.drawable.star_big_on)) .setContent(this)); tabHost.addTab(tabHost.newTabSpec("tab3") .setIndicator("destroy") .setContent(new Intent(this, Controls2.class) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP))); tabHost.addTab(tabHost.newTabSpec("tab4") .setIndicator("layout") .setContent(R.id.view4)); } public View createTabContent(String tag) { final TextView tv = new TextView(this); tv.setText("Content for tab with tag " + tag); return tv; } }Here are some of the highlights of the code:11.3. Layouts
Layouts are Android's solution to the variety of screens that come on Android devices: they can have different pixel densities, different dimensions, and different aspect ratios. Typical Android devices, such as the HTC G1 mobile phone, even allow changing the screen orientation (portrait or landscape) while applications are running, so the layout infrastructure needs to be able to respond on the fly. Layouts are intended to give developers a way to express the physical relationship of Views as they are drawn on the screen. As Android inflates the Layout, it uses the developer requests to come up with a screen layout that best approximates what the developer has asked for.Looking a little deeper, layouts in Android are in the form of a tree, with a single root and a hierarchy of Views. Look back at any of the XML Layout files in the previous section and you'll see that the XML tags create just such a hierarchy, with a screen Layout as the root of the tree. Each View in the tree is termed the parent of the Views it contains and the child of the View that contains it. Layout is a two-pass process:
Measure pass
Layout pass- Again traversing the tree from the root, each parent View uses the available layout information to position its children as requested. If the requests can't be followed explicitly, Android does its best to make everything fit on the screen. If there are no requests given, it uses a default set of layout parameters. Each parent can pass layout information on to its children, telling them where they are positioned and what screen dimensions they have been granted (they might get less than they requested).
A Layout is a View itself, so there's nothing wrong with having multiple Layouts in a single layout XML file—they just have to be arranged in a hierarchy. So it's perfectly valid to have a vertical LinearLayout that includes a TableLayout as one of its rows. You'll learn a lot more about layouts in Chapter 12.11.3.1. Frame Layout
The Frame Layout is sort of a null layout specification. It reserves space on the screen for a single View to be drawn, and the View is always located at the upper left of the space. There is no way to specify a different location for the View, and there can be only one View in the Layout. If more than one View is defined in the layout file, they are just drawn on top of each other, all pinned to the upper-left corner.11.3.2. LinearLayout
LinearLayouts are used extensively in Android applications, and we used them in example code earlier. A LinearLayout asks that the contained Views be layed out as either a series of rows (vertical LinearLayout) or a series of columns (horizontal LinearLayout). In a vertical LinearLayout, all the rows are the same width (the width of the widest child). In a horizontal LinearLayout, there is one row of Views, all the same height (the height of the tallest child).Figure 11-11 shows an example of a vertical LinearLayout, and Figure 11-12 is an example of a horizontal one. Both have EditText Views as children. Example 11-9 shows the XML resource file that produces the vertical layout, and Example 11-10 shows the file that created the horizontal one.Example 11-9. Vertical LinearLayout resource file
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="EditText1" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="EditText2" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="EditText3" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="EditText4" /> </LinearLayout>Figure 11-11. Vertical LinearLayout
Example 11-10. Horizontal LinearLayout resource file
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <EditText android:layout_width="wrap_content" android:layout_height="fill_parent" android:text="E1" /> <EditText android:layout_width="wrap_content" android:layout_height="fill_parent" android:text="E2" /> <EditText android:layout_width="wrap_content" android:layout_height="fill_parent" android:text="E3" /> <EditText android:layout_width="wrap_content" android:layout_height="fill_parent" android:text="E4" /> </LinearLayout>Figure 11-12. Horizontal LinearLayout
The horizontal layout might not look exactly as you would think: how come E4 is narrower than the other three? The answer is that there is a default minimum width for an EditText. If you build and run the horizontal example and type something into EditText E1, you'll see that it expands in width as the line gets longer, which is just what we asked for with android:layout_width="wrap_content".In addition to the usual dimensional parameters for child Views (width, height, padding), you can include a weight for each child (attribute android:layout_weight=;weight). The weight tells the layout manager how you want to use unfilled space, and defaults to a value of 0. If you specify children with weights greater than zero, the layout manager will allocate unused space to each child in proportion to its weight.Figure 11-13 shows an example of a LinearLayout containing four EditTexts. The first two have no weights assigned. EditText3 has a weight of 1 and EditText4 has a weight of 2. The effect is to make EditText4 twice as big as EditText3, while EditText1 and EditText2 just split whatever space the layout leaves over.Figure 11-13. Weighted LinearLayout
11.3.3. TableLayout
A TableLayout is just what you'd expect: it lays out the included Views in the form of a table (similar to an HTML table). We can create a table of TextViews to show how you would create that kind of screen for an application. Here's an example TableLayout XML file:<?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tblJobs" android:layout_width="fill_parent" android:layout_height="wrap_content" > <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content"> <Button android:text="Cell 11" android:id="@+id/btnCel11" android:layout_width="20dip" android:layout_height="wrap_content" /> <TextView android:id="@+id/txtCell12" android:layout_width="20dip" android:layout_height="wrap_content" android:text="Cell 12" /> <TextView android:id="@+id/txtCell13" android:layout_width="20dip" android:layout_height="wrap_content" android:text="Cell 13" /> <TextView android:id="@+id/txtCell14" android:layout_width="20dip" android:layout_height="wrap_content" android:text="Cell 14" /> </TableRow> <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content"> <Button android:text="Cell 21" android:id="@+id/btnCo21" android:layout_width="80dip" android:layout_height="wrap_content" /> <TextView android:id="@+id/txtCell22" android:layout_width="80dip" android:layout_height="wrap_content" android:text="Cell 22" /> <TextView android:id="@+id/txtCell23" android:layout_width="80dip" android:layout_height="wrap_content" android:text="Cell 23" /> <TextView android:id="@+id/txtCell24" android:layout_width="80dip" android:layout_height="wrap_content" android:text="Cell 24" /> </TableRow> </TableLayout>Figure 11-14 shows the resulting layout on the emulator screen.Figure 11-14. TableLayout
The structure of the XML file is pretty evident: the TableLayout tags contain a list of TableRows that in turn contain the Views you want to appear on each line of the table. Notice that the layout_width values are different in the two rows—all the widths in the first row are specified as 20dip, whereas the widths in the second row are specified as 28dip—yet the columns line up on the screen. To preserve the look of a table, Android makes each column as wide as the widest cell in that column.Of course, the cells are addressable from your Java code, and you can add rows programmatically to the table, if that's what your application needs to do.11.3.4. AbsoluteLayout
An AbsoluteLayout puts views on the screen wherever you tell it to. It doesn't try to resize anything, and it doesn't try to line anything up; it just puts things where it's told. You might think that it would be an easy type of layout to use, since you don't have to second-guess how the layout manager is going to rearrange things on your screen, but in practice the use of AbsoluteLayout is a bad idea for almost all applications. You usually want your application to run on as many Android devices as possible, and the strength of the Android layout manager is that it will automatically adapt your screen layout from device to device. AbsoluteLayout bypasses most of the layout manager, and while your application may look perfect on the device you used for development, the odds are very good that it will look terrible on other Android devices.That warning aside, let's take a look at an AbsoluteLayout XML file:<?xml version="1.0" encoding="utf-8"?> <AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Upper Left" android:layout_x="0.0px" android:layout_y="0.0px" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Middle" android:layout_x="140.0px" android:layout_y="200.0px" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Lower Right" android:layout_x="240.0px" android:layout_y="400.0px" /> </AbsoluteLayout>As with any dimension in a layout file, the positions can be expressed in pixels (px), device-independent pixels (dp), scaled pixels (sp), inches (in), or millimeters (mm), and the dimension has to be a floating-point number. (For more about expressing sizes, see Dimensions in Android in Chapter 4.)Figure 11-15 shows the resulting screen layout. Obviously, the position (0, 0) is the upper-left corner of the display, and the View is properly flush with the corner. The lower-right corner on the emulator is supposed to be (320, 480), but the View appears to be a little shy of that in both dimensions.Figure 11-15. AbsoluteLayout
Just to caution against the use of AbsoluteLayout again, we suggest you try changing the emulator skin to show the screen in landscape mode (enter emulator-skin HVGA-L
from a command or terminal window before you run the application), and you can see in Figure 11-16 that the application no longer looks right.Figure 11-16. Same AbsoluteLayout in landscape mode
11.3.5. RelativeLayout
We've used RelativeLayout, often in combination with LinearLayout, throughout the MJAndroid application. The advantage of RelativeLayout is that you can express the relative positioning of the Views in the screen, and the layout manager will do its best to fit them all on the screen in the proper relations. An example follows:<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/txtText1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Text1" android:gravity="top" android:layout_alignParentRight="true" /> <TextView android:id="@+id/txtText2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Text2" android:layout_below="@+id/txtText1" /> <Button android:id="@+id/btnButton1" android:layout_width="150dp" android:layout_height="wrap_content" android:text="Button1" android:layout_below="@+id/txtText2" /> <Button android:id="@+id/btnButton2" android:layout_width="150dp" android:layout_height="100dp" android:text="Button2" android:layout_toRightOf="@+id/btnButton1" android:layout_alignTop="@+id/btnButton1" /> </RelativeLayout>Figure 11-17 shows what this looks like in portrait mode (the emulator default), and Figure 11-18 shows it in landscape mode. The layout manager has adjusted the arrangements in each case to match the layout hints we gave in the XML.Figure 11-17. RelativeLayout in portrait mode
Figure 11-18. RelativeLayout in landscape mode
No comments:
Post a Comment