Creating the Layout: RelativeLayout and TableLayout
In this lesson, we create the user interface using declarative XML files, RelativeLayout and TableLayout.
The first step in writing our program is to create the GUI (graphical user interface). We are going to do this by editing the xml in the file main.xml. It is also possible to create the user view (presentation) "programmatically" in Java. By declaring our user interface in main.xml, the user view is "freeze dried" and separate from the Java application code. Not surprisingly, using xml in this way to create the GUI is by "declaration." Separating out the user view in this way allows us to "create XML layouts for different screen orientations, different device screen sizes, and different languages." It is also possible to alter this freeze dried user interface at run-time. We can "re-hydrate" the view explicitly by "inflating" the view.
We can construct our interface using the drag and drop graphical layout window which will generate the appropriate changes to main.xml. It is possible to then directly edit the main.xml file for more direct control of the code. You can experiment with the editors by dragging a text box onto the graphical view, examining the new code generated by the graphical editor in main.xml, and then reverting the changes by editing out the newly generated code directly in the main.xml window. Here is the original code in main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
Figure 1. The default main.xml code
Embedded Strings vs string.xml
Separating out the actual string values into yet another file, strings.xml allows you to localize your application in the future. Since all the string values are in one place, you could create another strings.xml file in the appropriate language for the new locale if needed. On the other hand, it is much easier to embed the string values into main.xml when prototyping on the fly. So instead of:
android:text="@string/hello"
You could do:
android:text="Hello World."
If you use very descriptive @string names, you should be able to replace the text strings in main.xml with string placeholders and edit the strings.xml file at a later date when you are done prototyping.
General Technique
To create your own layout, delete all of the code in main.xml except line 1. Then drag and drop a layout onto the view using the graphical view editor. Now go look at the main.xml code. Go back to the graphical view editor and drop the view elements that you want to use onto the view. Finally, go back to the XML view and edit the XML until you are happy with the appearance of your presentation.
Relative Layout
I deleted the default main.xml code (except for line one). The linear layout is great if you want to stack view elements, but not so great if you need horizontal spacing. Here is an experiment using Relative Layout. The code looks like this in the main.xml window.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/RelativeLayout01" android:layout_width="fill_parent" android:layout_height="wrap_content">
<EditText android:text="Plain Text Here" android:id="@+id/EditText01" android:layout_width="fill_parent" android:layout_height="wrap_content"></EditText>
<Button android:text="Confuse Text"
android:id="@+id/Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/EditText01"
android:layout_alignParentRight="true">
</Button>
<Button android:text="Get Plain Text"
android:id="@+id/Button02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/Button01"
android:layout_alignTop="@id/Button01">
</Button>
<EditText android:text="Confused Text Here"
android:id="@+id/EditText02"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/Button01">
</EditText>
</RelativeLayout>
Figure 2. XML Code using RelativeLayout
Here is the new interface using RelativeLayout as it appears in the graphical view mode.
Figure 3. The user view (presentation) using RelativeLayout
Table Layout
If you are familiar with HTML tables, then using the TableLayout may be more intuitive. In the next sample, we use an embedded row to create a slightly more sophisticated layout. The graphical editor DOES support drag and drop into columns.
<?xml version="1.0" encoding="utf-8"?>
<TableLayout android:id="@+id/TableLayout01" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<TableRow>
<EditText android:layout_height="wrap_content"
android:text="Password"
android:id="@+id/EditText03"
android:layout_width="wrap_content">
</EditText>
<TextView android:gravity="right"
android:text="PW"
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</TableRow>
<TableRow>
<EditText android:text="Enter Plain Text"
android:id="@+id/EditText02"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</EditText>
<TextView android:text="PT"
android:id="@+id/TextView02"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</TableRow>
<TableRow>
<TableRow> // embedded row
<Button android:text="Confuse Text"
android:id="@+id/Button02"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
<Button android:text="Get Plain Text"
android:id="@+id/Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
</TableRow>
</TableRow>
<TableRow>
<EditText android:text="Enter Confused Text"
android:id="@+id/EditText01"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</EditText>
<TextView android:text="CT"
android:id="@+id/TextView03"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</TableRow>
</TableLayout>
Figure 4. The XML Code using an embedded row.
Here is our slightly improved layout using an embedded row.
Figure 5a. The user view (presentation) using an embedded row to display two buttons in the first column in a layout with a second column of text.
It is also possible to embed a TableLayout in a TableRow!
<TableRow>
<TableLayout android:id="@+id/TableLayout02" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android"> // embedded table!
<TableRow>
<Button android:text="Confuse Text"
android:id="@+id/Button02"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
<Button android:text="Get Plain Text"
android:id="@+id/Button03"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
<Button android:text="Clear Text"
android:id="@+id/Button01"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
</TableRow>
</TableLayout>
</TableRow>
Figure 5b. Using an embedded table to display three buttons in the first column in a layout with a second column of text.
Tab Layout
For whatever reason, I cannot get the Android samples for Tab Layout (TabHost, TabWidget, FrameLayout) to parse successfully. Go figure. When I drag a TabWidget onto an empty panel I get the error: "The following classes could not be found: - TabWidget." There may be a problem with the TabWidget in Eclipse ADT Plugin. Another error I encountered was: View found with id 'tabs' is 'com.android.layoutlib.bridge.MockView.' This error has also been reported.
@id vs @+id
If you are wondering what the difference is between @id and @+id, the first usage is for a pre-defined resource provided by the Android Framework such as @android:id/empty. The second usage is for a local new resource name to be added to the R.java file as in @+id/ok_button.
Editing Height of the View Elements
You can change the height of a specific view element using absolute values in three units sp (scaled pixels), dp (density-independent pixels) and px (just plain pixels:)) You can also set the height of a specific view element using one of the pre-defined constants: fill_parent, match_parent, or wrap_content.
Figure 6. Values for view element height
Use the variable layout_width to set the value as in:
android:layout_height="65sp"
Adding a Scroll Bar
You can add a bar that scrolls by wrapping the entire layout into a ScrollView as in:
<ScrollView android:id="@+id/ScrollView01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<TableLayout android:id="@+id/TableLayout01" android:layout_width="fill_parent" android:layout_height="fill_parent">
...
</TableLayout>
</ScrollView>
Figure 6. Wrapping a TableLayout in a ScrollView
As the final step, we separate out the string values to strings.xml and wrap our TableLayout inside a ScrollView to complete this phase of our project.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView android:id="@+id/ScrollView01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<TableLayout android:id="@+id/TableLayout01"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TableRow>
<EditText android:layout_height="wrap_content"
android:text="@string/password_edit_text"
android:id="@+id/EditTextPassword"
android:layout_width="wrap_content">
</EditText>
<TextView android:gravity="right"
android:text="@string/password_text_view"
android:id="@+id/TextViewPassword"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</TableRow>
<TableRow>
<EditText android:text="@string/plain_text_edit_text"
android:id="@+id/EditTextPlainText"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</EditText>
<TextView android:text="@string/plain_text_text_view"
android:id="@+id/TextViewPlainText"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</TableRow>
<TableRow>
<TableRow> // embedded Table!
<Button android:text="@string/confuse_text_button"
android:id="@+id/ButtonConfuseText"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
<Button android:text="@string/get_plain_text_button"
android:id="@+id/ButtonPlainText"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
</TableRow>
</TableRow>
<TableRow>
<EditText android:text="@string/enter_confused_text_edit_text"
android:id="@+id/EditTextConfusedText"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</EditText>
<TextView android:text="@string/confused_text_text_view"
android:id="@+id/TextViewConfusedText"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</TableRow>
</TableLayout>
</ScrollView>
Figure 8. Main.xml after wrapping in a ScrollView and separating out the string values.
Adding a Fixed Header and a Fixed Footer to a Scrolling View
It is possible to have the text scroll in the center of the screen with a fixed header and footer. This is done by embedding a scroll view into a relative layout as in:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:id="@+id/relativeLayout1"
android:layout_width="wrap_content">
<TextView
android:text="Header"
android:id="@+id/textViewHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
<TextView
android:layout_alignParentBottom="true"
android:text="Footer"
android:id="@+id/textViewFooter"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
<ScrollView
android:layout_above="@id/textViewFooter"
android:layout_below="@id/textViewHeader"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
>
<LinearLayout
android:layout_below="@id/textViewHeader"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:text="@string/about_tlh_body"
android:id="@+id/textViewBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</LinearLayout>
</ScrollView>
</RelativeLayout>
Here is the result:
The header and footer will not move as the user scrolls the text. Thanks to "Wireless Designs" for the solution on StackOverflow.
Floating Buttons
Here is a screen shot of two floating buttons over a scroll view created by embedding a scroll view in a relative layout.
Have fun,
JAL