programming4us
programming4us
MOBILE

Beginning Android 3 : Working with Containers - LinearLayout Example & The Box Model

7/13/2011 5:34:50 PM
Containers pour a collection of widgets (and possibly child containers) into specific structures you like. If you want a form with labels on the left and fields on the right, you need a container. If you want OK and Cancel buttons to be beneath the rest of the form, next to one another, and flush to right side of the screen, you need a container. Just from a pure XML perspective, if you have multiple widgets (beyond RadioButton widgets in a RadioGroup), you need a container just to have a root element in which to place the widgets.

Most GUI toolkits have some notion of layout management, frequently organized into containers. In Java/Swing, for example, you have layout managers like BoxLayout and containers that use them (e.g., Box). Some toolkits, such as XUL and Flex, stick strictly to the box model, figuring that any desired layout can be achieved through the right combination of nested boxes. Android, through LinearLayout, also offers a box model, but in addition supports a range of containers that provide different layout rules.

Thinking Linearly

As just noted, LinearLayout is a box model—widgets or child containers are lined up in a column or row, one after the next. This works similarly to FlowLayout in Java/Swing, vbox and hbox in Flex and XUL, and so forth.

Flex and XUL use the box as their primary unit of layout. If you want, you can use LinearLayout in much the same way, eschewing some of the other containers. Getting the visual representation you want is mostly a matter of identifying where boxes should nest and which properties those boxes should have, such as their alignment relative to other boxes.

1. LinearLayout Concepts and Properties

To configure a LinearLayout, you have five main areas of control besides the container's contents: the orientation, the fill model, the weight, the gravity, and the padding.

1.1. Orientation

Orientation indicates whether the LinearLayout represents a row or a column. Just add the android:orientation property to your LinearLayout element in your XML layout, and set the value to be horizontal for a row or vertical for a column.

The orientation can be modified at runtime by invoking setOrientation() on the LinearLayout, supplying it either HORIZONTAL or VERTICAL.

1.2. Fill Model

Let's imagine a row of widgets, such as a pair of radio buttons. These widgets have a "natural" size based on their text. Their combined size probably does not exactly match the width of the Android device's screen—particularly since screens come in various sizes. We then have the issue of what to do with the remaining space.

All widgets inside a LinearLayout must supply android:layout_width and android:layout_height properties to help address this issue. These properties' values have three flavors:

  • You can provide a specific dimension, such as 125dip to indicate the widget should take up exactly a certain size.

  • You can provide wrap_content, which means the widget should fill up its natural space, unless that is too big, in which case Android can use word-wrap as needed to make it fit.

  • You can provide fill_parent, which means the widget should fill up all available space in its enclosing container, after all other widgets are taken care of.

The latter two flavors are the most common, as they are independent of screen size, allowing Android to adjust your view to fit the available space.

NOTE

In API level 8 (Android 2.2), fill_parent was renamed to match_parent, for unknown reasons. You can still use fill_parent, as it will be supported for the foreseeable future. However, at such point in time as you are supporting only API level 8 or higher (e.g., android:minSdkVersion="8" in your manifest), you should probably switch over to match_parent.

1.3. Weight

But what happens if we have two widgets that should split the available free space? For example, suppose we have two multiline fields in a column, and we want them to take up the remaining space in the column after all other widgets have been allocated their space.

To make this work, in addition to setting android:layout_width (for rows) or android:layout_height (for columns) to fill_parent, you must also set android:layout_weight. This property indicates the proportion of the free space that should go to that widget. For example, if you set android:layout_weight to be the same nonzero value for a pair of widgets (e.g., 1), the free space will be split evenly between them. If you set it to be 1 for one widget and 2 for the other widget, the second widget will use up twice the free space that the first widget does. And so on. The weight for a widget is 0 by default.

Another pattern for using weights is if you want to allocate sizes on a percentage basis. To use this technique for, say, a horizontal layout, do the following:

  • Set all the android:layout_width values to be 0 for the widgets in the layout.

  • Set the android:layout_weight values to be the desired percentage size for each widget in the layout.

  • Make sure all those weights add up to 100.

1.4. Gravity

By default, everything in a LinearLayout is left- and top-aligned. So, if you create a row of widgets via a horizontal LinearLayout, the row will start flush on the left side of the screen. If that is not what you want, you need to specify a gravity value. Using android:layout_gravity on a widget (or calling setGravity() at runtime on the widget's Java object), you can tell the widget and its container how to align it vis-à-vis the screen.

For a column of widgets, common gravity values are left, center_horizontal, and right for left-aligned, centered, and right-aligned widgets, respectively.

For a row of widgets, the default is for them to be aligned so their text is aligned on the baseline (the invisible line that letters seem to "sit on"). You can specify a gravity of center_vertical to center the widgets along the row's vertical midpoint.

1.5. Margins

By default, widgets are tightly packed next to each other. You can change this via the use of margins, a concept that is similar to that of padding.

The difference between padding and margins is apparent only for widgets with a nontransparent background. For widgets with a transparent background—like the default look of a TextView—padding and margins have similar visual effect, increasing the space between the widget and adjacent widgets. For widgets with a nontransparent background—like a Button—padding is considered to be inside the background, while margins are considered to be outside the background. In other words, adding padding will increase the space between the contents (e.g., the caption of a Button) and the edges, while adding margins increases the empty space between the edges and adjacent widgets.

Margins can be set in XML, either on a per-side basis (e.g., android:layout_marginTop) or on all sides via android:layout_margin. As with padding, the value of any of these is a dimension—a combination of a unit of measure and a count, such as 5px for 5 pixels.

2. LinearLayout Example

Let's look at an example (Containers/Linear) that shows LinearLayout properties set both in the XML layout file and at runtime. Here is the layout:

<?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"
>
<RadioGroup android:id="@+id/orientation"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dip">
<RadioButton
android:id="@+id/horizontal"
android:text="horizontal" />
<RadioButton
android:id="@+id/vertical"
android:text="vertical" />
</RadioGroup>
<RadioGroup android:id="@+id/gravity"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dip">
<RadioButton
android:id="@+id/left"
android:text="left" />
<RadioButton
android:id="@+id/center"
android:text="center" />
<RadioButton
android:id="@+id/right"
android:text="right" />
</RadioGroup>
</LinearLayout>


Note that we have a LinearLayout wrapping two RadioGroup sets. RadioGroup is a subclass of LinearLayout, so our example demonstrates nested boxes as if they were all LinearLayout containers.

The top RadioGroup sets up a row (android:orientation = "horizontal") of RadioButton widgets. The RadioGroup has 5dip of padding on all sides, separating it from the other RadioGroup, where dip stands for density-independent pixels. The width and height are both set to wrap_content, so the radio buttons will take up only the space that they need.

The bottom RadioGroup is a column (android:orientation = "vertical") of three RadioButton widgets. Again, we have 5dip of padding on all sides and a natural height (android:layout_height = "wrap_content"). However, we have set android:layout_width to be fill_parent, meaning the column of radio buttons claims the entire width of the screen.

To adjust these settings at runtime based on user input, we need some Java code:

package com.commonsware.android.linear;

import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.text.TextWatcher;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.EditText;

public class LinearLayoutDemo extends Activity
implements RadioGroup.OnCheckedChangeListener {
RadioGroup orientation;
RadioGroup gravity;

@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);

orientation=(RadioGroup)findViewById(R.id.orientation);
orientation.setOnCheckedChangeListener(this);
gravity=(RadioGroup)findViewById(R.id.gravity);
gravity.setOnCheckedChangeListener(this);
}

public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.horizontal:
orientation.setOrientation(LinearLayout.HORIZONTAL);
break;

case R.id.vertical:
orientation.setOrientation(LinearLayout.VERTICAL);
break;


case R.id.left:
gravity.setGravity(Gravity.LEFT);
break;

case R.id.center:
gravity.setGravity(Gravity.CENTER_HORIZONTAL);
break;

case R.id.right:
gravity.setGravity(Gravity.RIGHT);
break;
}
}
}

In onCreate(), we look up our two RadioGroup containers and register a listener on each, so we are notified when the radio buttons change state (setOnCheckedChangeListener(this)). Since the activity implements OnCheckedChangeListener, the activity itself is the listener.

In onCheckedChanged() (the callback for the listener), we see which RadioButton had a state change. Based on the clicked-upon item, we adjust either the orientation of the first LinearLayout or the gravity of the second LinearLayout.

Figure 1 shows the result when the demo is first launched inside the emulator.

Figure 1. The LinearLayoutDemo sample application, as initially launched

If we toggle on the "vertical" radio button, the top RadioGroup adjusts to match, as shown in Figure 2.

Figure 2. The same application, with the vertical radio button selected

If we toggle the "center" or "right" radio button, the bottom RadioGroup adjusts to match, as shown in Figures 3 and 4.

Figure 3. The same application, with the vertical and center radio buttons selected

Figure 4. The same application, with the vertical and right radio buttons selected

3. The Box Model

Some GUI frameworks treat everything as boxes—what Android calls LinearLayout containers. In Flex and XUL, for example, you create boxes and indicate how big they should be, as a percentage of the available space, and then you put widgets in the boxes. A similar pattern exists in Android for LinearLayout, as is demonstrated in the Containers\LinearPercent project.

Here we have a layout XML file that contains a vertical LinearLayout wrapping three Button widgets:

<?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"
>
<Button
android:text="Fifty Percent"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="50"
/>
<Button
android:text="Thirty Percent"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="30"
/>

<Button
android:text="Twenty Percent"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="20"
/>
</LinearLayout>

Each of the three widgets will take up a certain percentage of the vertical space for the LinearLayout. Since the LinearLayout is set to fill the screen, this means that the three widgets will divide up the screen based on their requested percentages.

To request a percentage, each Button does the following:

  • Sets its android:layout_height to be 0dip (note that we use height here because it is a vertical LinearLayout we are subdividing)

  • Sets its android:layout_weight to be the desired percentage (e.g., android:layout_weight="50")

So long as the weights sum to 100, as they do in this case, you will get your desired breakdown by percentage, as shown in Figure 5.

Figure 5. A LinearLayout split among three Buttons by percentage
Other  
  •  iPhone Application Development : Reading and Writing User Defaults (part 2) - Implementing System Settings
  •  iPhone Application Development : Reading and Writing User Defaults (part 1) - Creating Implicit Preferences
  •  - Mobile Application Security : SMS Security - Overview of Short Message Service
  •  - Mobile Application Security : Bluetooth Security - Bluetooth Security Features
  •  Integrating Your Application with Windows Phone 7
  •  Introducing Windows Phone 7 Photo Features (part 2) - Using a Chooser to Open Photos & Saving Photos to the Phone
  •  Introducing Windows Phone 7 Photo Features (part 1) - Using a Chooser to Take Photos
  •  Mobile Application Security : Bluetooth Security - Bluetooth Technical Architecture
  •  Mobile Application Security : Bluetooth Security - Overview of the Technology
  •  Windows Phone 7 Development : Push Notifications - Implementing Cloud Service to Track Push Notifications
  •  
    Video
    PS4 game trailer XBox One game trailer
    WiiU game trailer 3ds game trailer
    Top 10 Video Game
    -   Minecraft Mods - MAD PACK #10 'NETHER DOOM!' with Vikkstar & Pete (Minecraft Mod - Mad Pack 2)
    -   Minecraft Mods - MAD PACK #9 'KING SLIME!' with Vikkstar & Pete (Minecraft Mod - Mad Pack 2)
    -   Minecraft Mods - MAD PACK #2 'LAVA LOBBERS!' with Vikkstar & Pete (Minecraft Mod - Mad Pack 2)
    -   Minecraft Mods - MAD PACK #3 'OBSIDIAN LONGSWORD!' with Vikkstar & Pete (Minecraft Mod - Mad Pack 2)
    -   Total War: Warhammer [PC] Demigryph Trailer
    -   Minecraft | MINIONS MOVIE MOD! (Despicable Me, Minions Movie)
    -   Minecraft | Crazy Craft 3.0 - Ep 3! "TITANS ATTACK"
    -   Minecraft | Crazy Craft 3.0 - Ep 2! "THIEVING FROM THE CRAZIES"
    -   Minecraft | MORPH HIDE AND SEEK - Minions Despicable Me Mod
    -   Minecraft | Dream Craft - Star Wars Modded Survival Ep 92 "IS JOE DEAD?!"
    -   Minecraft | Dream Craft - Star Wars Modded Survival Ep 93 "JEDI STRIKE BACK"
    -   Minecraft | Dream Craft - Star Wars Modded Survival Ep 94 "TATOOINE PLANET DESTRUCTION"
    -   Minecraft | Dream Craft - Star Wars Modded Survival Ep 95 "TATOOINE CAPTIVES"
    -   Hitman [PS4/XOne/PC] Alpha Gameplay Trailer
    -   Satellite Reign [PC] Release Date Trailer
    Game of War | Kate Upton Commercial
    programming4us
     
     
    programming4us