Chapter 6. Managing Layout

One of the key features of Flex is its ability to simplify application layout. Traditional application development requires writing layout code, or working with layout components in a nonintuitive manner. With MXML and Flex’s layout containers, you can lay out most applications without having to write a single line of custom layout code.

In this chapter, we will provide an overview of Flex layout containers and discuss the layout rules they use. We will also cover how to work with containers and children, how to nest containers, and how to build fluid interfaces.

Flex Layout Overview

Container components are the basis of how Flex provides layout logic. At the most basic level, the Application class is a container, and subitems within the Application class (tag) are called children. In MXML, placing nodes within a container declaration signifies that the objects are instantiated and are added to the container as children, and the container automatically handles their positioning and sizing.

For example, in the following code two children are added to the Application container—a TextInput instance and a Button instance:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:TextInput/>
    <mx:Button label="Submit"/>
</mx:Application>

Note

If you are using Flex Builder, the default MXML template sets the layout property of the root Application instance to absolute; when not specified, the default value vertical is used. The layout property of the Application container controls how children are positioned and sized, and if the value is set, the examples may not work as expected.

In the preceding code, you added two children to the Application container by simply placing the children as subnodes of the container using MXML. This adds the children to the container’s display list, which, under the hood, is the same display list Flash Player uses. Containers allow for several different types of layout management. In this example, we’re allowing the container to apply its own default layout rules to stack the children vertically. Other layout containers have different layout rules, and some containers even allow you to specify fixed coordinates for children. We’ll see lots of examples of these throughout the chapter. Containers also are noninteractive objects, in that they don’t receive keyboard/mouse input themselves; instead, they house children that receive all input.

Note

The tabEnabled property built into Flash Player is part of the InteractiveObject class from which containers inherit. The property controls whether an object can receive user focus, which is not the purpose of containers. So, by default, the tabEnabled property of containers is usually set to false. The tabChildren property, inherited from DisplayObjectContainer, is what instructs Flash Player to allow children of a container to receive user focus. With these properties set, children of containers will receive focus while the container itself will not.

In the previous code sample, we added children to a container using MXML. You can also do this using ActionScript, as shown in Example 6-1. Understanding the code in Example 6-1 will give you insight into how MXML works.

Example 6-1. Adding children to a container using ActionScript

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="addItems()">
    <mx:Script>
        <![CDATA[
            import mx.controls.Button;
            import mx.controls.TextInput;
            private function addItems():void
            {
                var ti:TextInput = new TextInput();
                this.addChild(ti);
                var btn:Button = new Button();
                btn.label = "Submit";
                this.addChild(btn);
            }
        ]]>
    </mx:Script>
</mx:Application>

As you can see in Example 6-1, the ActionScript code is more verbose than the MXML code. This is a prime example of why MXML is ideal for rapidly developing application user interfaces. In general, you should write application layout in MXML whenever possible, and choose ActionScript only when you want to do more at runtime, such as add an arbitrary number of children, or in cases where MXML doesn’t provide enough control to achieve the desired layout. Keep in mind that you can mix both ActionScript and MXML in an application’s layout, so it’s important for you to learn how to code in both ways. If you find that a portion of your layout requires dynamic control over child instantiation, you may opt to handle that with ActionScript and the display list API and handle the rest of the layout using MXML.

Working with Children

In addition to adding children, you also have the ability to remove, reorder, and retrieve the children of a container’s display list. In Flex, container children are synonymous with children that inherit from the UIComponent class.

Note

In Flex, container children must implement the IUIComponent interface. Because the UIComponent class implements this interface, we typically will refer to UIComponent-based components as valid container children. If you plan to implement a custom child, you’ll need to ensure you implement IUIComponent and inherit from flash.display.DisplayObject for containers to handle the child properly.

Setting up the initial state of a container via MXML is simple enough, but managing change afterward requires a better understanding of the ActionScript display list API. The methods addChild(), addChildAt(), getChildAt(), getChildByName(), getChildIndex(), getChildren(), removeAllChildren(), contains(), and setChildIndex(), as well as the numChildren property, are the Container class members related to working with children. Most of them are self-explanatory. Example 6-2 takes the last child and moves it to the first position in a container’s children when the button is pressed.

Example 6-2. Reordering children using the display list API

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
        private function moveToTheBeginning():void
        {

            // Retrieve the index of the last child. Child indexes are zero-based
            var lastChildIndex:int = tileOfLabels.numChildren - 1;

            // Get a reference to the last child
            var child:DisplayObject = tileOfLabels.getChildAt(lastChildIndex);

            // Change the index of the child
            tileOfLabels.setChildIndex(child,0);
        }
        ]]>
    </mx:Script>
    <mx:Tile id="tileOfLabels">
        <mx:Label text="1"/>
        <mx:Label text="2"/>
        <mx:Label text="3"/>
        <mx:Label text="4"/>
        <mx:Label text="5"/>
        <mx:Label text="6"/>
    </mx:Tile>
    <mx:Button label="Move to the beginning" click="moveToTheBeginning()"/>
</mx:Application>

This basic example covers a few important concepts. First, the initial layout contains six buttons within a Tile container, each labeled according to its order within the Tile. A button at the bottom is used to call a function to move the last child to the beginning of the Tile container. In moveToTheBeginning(), the index of the last child is retrieved, which is zero-based. Next, a reference to the last child in the display list is obtained using getChildAt(). After the last child index is retrieved, setChildIndex() is called and passes a reference to the button instance and a new index.

Note

A good thing to keep in mind is that although MXML is ideal for laying out an application, you have full control of that layout at runtime using ActionScript. For instance, in ActionScript you can achieve tasks such as hiding containers depending on user interaction, as well as reordering containers and changing their sizes.

If you are familiar with how Flash Player handles display objects, handling children within containers should already look familiar as Flex implements the same API for handling child objects that Flash Player does. Note, however, that although the API between Flash Player and Flex is the same, there is a subtle difference in how Flash Player and Flex handle children. Flex containers, unlike Flash Player, do not return at runtime a reference to all children that are actually part of a container. Children in Flex are divided into two types: chrome and content. Children used to draw the outline, header, or other unique rendered items related to the container are added automatically by the component, are hidden from the display list API in Flex, and are referred to as chrome children. Children added to a container by the developer, such as Button or Label, are content children. Flex differentiates the two to simplify the process of dealing with children, because typically a developer is more interested in the content children.

Note

Although Flex hides the chrome-related children, you can still access the complete display list and manipulate its children using the rawChildren container property. Flex provides this property to grant access to the entire display list, which you may need if you plan to manipulate chrome-related children or add display objects that do not implement IUIComponent. Working with rawChildren is an advanced topic that will require more understanding of Flash Player’s display list, which we do not cover in this book. Also, if you need to add a child that does not inherit from UIComponent, you may want to consider implementing the IUIComponent interface for the display object rather than working with rawChildren directly.

Container Types

Every container provided by the Flex framework has a set of rules by which it lays out its children. Flex uses these rules to measure the available space for children and to decide where children are positioned. A VBox container, for example, arranges its children vertically, placing only one item per row. Similarly, an HBox container arranges its children horizontally. Every container in Flex was designed with a purpose (see Figure 6-1). Knowing when to use a particular container is important as you build your Flex applications.

Class diagram of container components

Figure 6-1. Class diagram of container components

Table 6-1 lists the different container types in Flex and describes how each is used.

Table 6-1. The different Flex container types

Container type

Description

Application

This special container is the root of a Flex application. By default, it behaves like a VBox container. The layout property controls how children are laid out. The possible values are ContainerLayout.ABSOLUTE, ContainerLayout.VERTICAL, and ContainerLayout.HORIZONTAL. Setting the layout property to ABSOLUTE causes this container to behave as a Canvas container and allows you to specify absolute positions for each child. Setting the value to HORIZONTAL or VERTICAL causes the layout to behave like an HBox or VBox, respectively.

Box

Typically, you will not use this container directly, but it is an important container to understand because many containers base their layout rules on it. This container lays out its children, one after the other, in a single column (or row), depending on the direction of the property value. By default, the contents are laid out vertically. Possible values for the direction property are BoxDirection.VERTICAL and BoxDirection.HORIZONTAL. Box is the base implementation for the Application, VBox, HBox, ControlBar, HDividedBox, VDividedBox, NavBar, Panel, and TitleWindow containers.

Canvas

This container is for absolute positioning and constraint-based layout. Children are laid out using the x and y properties, and the top, bottom, right, left, verticalCenter, and horizontalCenter properties are used for achieving a constraint-based layout.

ControlBar

This container is used to provide a reserved region at the bottom of a Panel or TitleWindow container for placing children. This container lays out its content in the same way that an HBox does.

DividedBox

This container lays out children in the same way that Box does, except it places an interactive divider bar between each child. This container is ideal for separating regions of an application and allowing a user to resize regions by dragging a divider. As with Box, children can be laid out horizontally or vertically.

Form

This special container is designed specifically for laying out forms. It allows you to easily position form labels, headings, and input controls.

Grid

This container allows you to position children within columns and rows. This container’s behavior is very similar to that of HTML tables.

HBox

This container is derived from Box with the direction property set to BoxDirection.HORIZONTAL. Otherwise, it behaves as Box does.

HDividedBox

This container is derived from DividedBox with the direction property set to BoxDirection.HORIZONTAL by default.

Panel

This is a layout container that contains a chrome border with a title bar area. The content area by default behaves like a VBox. The layout property controls how children are laid out and the default value is ContainerLayout.VERTICAL. Additional possible values are ContainerLayout.ABSOLUTE and ContainerLayout.HORIZONTAL. Setting the layout property to ABSOLUTE causes this container to behave as a Canvas container. Setting the value to HORIZONTAL or VERTICAL causes the layout to behave like an HBox or VBox, respectively.

Tile

This container tiles children, and by default it tries to keep the number of rows and columns equal to each other. If the width or height property of the container is specified, the container will try to satisfy the available area. If no width or height is specified, it tries its best to keep the number of rows and columns equal to each other. The Tile container contains a direction property, which by default is set to VERTICAL. Possible values are TileDirection.VERTICAL and TileDirection.HORIZONTAL.

TitleWindow

Ideal for pop-up windows, TitleWindow inherits from Panel, with the addition of a button in the title bar to allow users to close the window.

VBox

This container is derived from Box with the direction property set to BoxDirection.VERTICAL. Otherwise, it behaves as Box does.

VDividedBox

This container is derived from DividedBox, with the direction property set to BoxDirection.VERTICAL by default.

Layout Rules

Many containers internally make use of two main layout rules: box and canvas. These rules define how containers internally implement child positioning and sizing. Understanding the different layout rules will help you to understand how layout containers work and to develop application layouts more effectively.

Layout rules are executed when children containers are initially instantiated, anytime children are added or removed, and whenever a container is resized. The only time that is not the case is when the autoLayout property is set to false. In this case, the layout rules execute only on initialization and when children are added or removed.

Note

Setting the autoLayout property to false still causes the container to measure and position children on initial rendering and when children are added or removed. However, this won’t cause the container to lay out its children again when the container is resized. This is beneficial in cases when you do not want to implement a liquid interface that automatically resizes or when the exact layout needs to remain the same when the container is resized.

It is also important to note that measuring and positioning in a container can sometimes be a processor-intensive process, so you may opt to set autoLayout to false to handle such cases as well.

When layout rules are executing, they go through two steps. First, the container measures the space needed for each child. This allows the container to decide how much space it needs to best fit all of its children, and it allows the container to decide where everything should be positioned if it is responsible for positioning the children. During this process, if a child’s includeInLayout property is set to false, the child will be ignored and won’t be factored into the positioning of children. In the second step, the container repositions and sizes the children as needed.

Note

Setting a child’s visible property alone to false does not exclude the child from the layout routines of a container, resulting in a layout where hidden children will occupy a space within the container even though it is not visible. In cases where this is not the desired behavior, setting the includeInLayout and visible properties of a child to false will instruct the container to ignore that child when deciding how to lay out children and will visually hide the child.

Box-based layout

Now that you understand a bit about how layout rules work, let’s discuss each type of layout rule in more detail. The containers HBox, VBox, HDividedBox, VDividedBox, ApplicationControlBar, and ControlBar all base their layout rules on those of the Box container. For this reason, you will often find people refer to these as box-based layout containers.

The box layout rule dictates that each child occupies its own row or column, depending on the direction under which the rule is operating. The two directions supported are vertical and horizontal. All box-based layout containers implement the direction property, and the default value of the direction property depends on the container. You can set the value of the direction property to horizontal or vertical using the constant values BoxDirection.HORIZONTAL and BoxDirection.VERTICAL.

In Box, the default direction is set to vertical, as shown in Figure 6-2. Each child occupies its own row, and the children are stacked one on top of the other.

A Box container with three child objects

Figure 6-2. A Box container with three child objects

If the width of the container is not specified, the container determines it by identifying the child with the largest width and adjusting its own width so that it can display the child with little or no clipping. If an explicit width is set, the container will adhere to the specified width, and if the width of the child objects exceeds the set width, by default the child objects will be clipped and a scroll bar will be displayed. In the same manner, the height of the container expands to allow all children to fit accordingly, unless an explicit height for the box-based container is set, at which point the container uses a scroll bar to allow the user access to all the children. Changing the number of children or their width or height at runtime causes the container to be marked for invalidation, which will cause the layout rule to be reevaluated and the children to be automatically repositioned and sized as required.

Note

In an attempt to minimize unnecessary redraws, which can cause severe performance degradation, Flex components mark parts of a component that need to be redrawn as invalid. We discuss invalidation in Chapter 19 in the context of component development, but the same process applies to all components in Flex, including containers.

When the box layout rule is operating with the direction property set to horizontal, the rules apply in the same way as when the direction property is set to vertical, except that the box attempts to grow and lay out children horizontally rather than vertically.

In this layout rule, the size of the container depends on a few factors:

  • If the container’s width and height are set, those values are used.

  • If the size that is set is smaller than the area needed for the children to be displayed, a scroll bar is automatically displayed unless verticalScrollPolicy and horizontalScrollPolicy are set to ScrollPolicy.OFF.

  • If no size is explicitly set, the container attempts to grow as needed within the available space. If enough space is not available, a scroll bar is used to allow the user access to the content.

Canvas-based absolute layout

The Canvas container implements the canvas-based layout rule. The canvas layout rule provides a lot of flexibility in attaining sophisticated layout while attempting to ensure that application layout routines perform well. That’s because you must provide all the positioning logic, which means that Flex doesn’t have to do all the work of measuring the Canvas container’s children and calculating optimal positions.

Canvas-based layout allows you to position children using explicit x and y coordinates. This allows you to accurately control the position of each child. Example 6-3 shows two children positioned using exact x and y positions, relative to the position of the Canvas itself.

Example 6-3. Absolute positioning using a Canvas container

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Canvas>
        <mx:Label x="0" y="50" text="Enter your name:"/>
        <mx:TextInput x="110" y="50"/>
    </mx:Canvas>
</mx:Application>

As you can see, the Canvas container is most suitable for reproducing a pixel-perfect layout. Unlike other containers, where layout rules set the positions of children, the Canvas container doesn’t prevent you from overlapping children. If the positions of the children need to change, you can handle it on your own by setting the x and y values at runtime.

Canvas-based constraint layout

Canvas also supports the ability to lay out children using what is called constraint-based layout. Constraint-based layout lets you lay out children at predefined positions relative to their parent or a constraint. This gives you the flexibility of positioning children in a predefined position while at the same time repositioning them as needed to satisfy the constraints set. This tends to be a more practical method of accurately positioning children than simply supplying a specific location, because it allows you to position children precisely within a plane and be able to reposition the children dynamically according to the constraints set. To position children using the constraint-based layout method you set one or several of their style properties—top, bottom, left, right, horizontalCenter, and verticalCenter—with a value based on the parent’s or constraint’s position in relation to the child. For now, we will focus on positioning in relationship to a parent, and later in this chapter we will see how we can use constraints for a more powerful layout. For example, when you set the right style to 10, you're positioning the child 10 pixels away from the right edge of the parent container. When the container is resized, the child automatically repositions itself 10 pixels from the right edge.

Example 6-4 shows two buttons positioned using container-based layout. One is positioned 10 pixels from the bottom right, and the other is positioned 10 pixels from the right. Both buttons will automatically be repositioned whenever the browser is resized.

Example 6-4. Positioning children using constraint-based layout

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Canvas width="100%" height="100%">
        <mx:Button right="10" label="Rightmost button"/>
        <mx:Button right="10" bottom="10" label="Right bottommost button"/>
    </mx:Canvas>
</mx:Application>

Figure 6-3 shows the results.

Results from , in which children are positioned using constraint-based layout

Figure 6-3. Results from Example 6-4, in which children are positioned using constraint-based layout

Note

In Example 6-4, two buttons are positioned within a Canvas. The first button is positioned 10 pixels away from the right edge, with the default y value of 0 (if a child doesn’t have its position set in a Canvas container, the default x and y values are 0, 0). The second button is positioned to the bottom right of the Canvas container. This is accomplished by setting the first button’s right style property to 10. The second button’s right and bottom style properties also are set to 10, thus resulting in the button being offset 10 pixels from the bottom-right edge of the parent container. Also note that the width and height properties of the Canvas container are set to 100%. This is to ensure that the canvas automatically resizes to occupy as much space as possible. Like all style properties, you can set constraint-based layout styles directly inline within MXML tags, via ActionScript using the setStyle() method, or via any other method that style properties support.

One final capability that constraint-based layout containers support is that of subdividing the container into regions and laying out the children according to those regions. The best way to think about this is to imagine a single plane that is divided into columns and rows, and within each table cell is a new parent for the children. You can achieve this by creating many instances of the Canvas container side by side or, optimally, by using a single Canvas container and dividing it into regions using ConstraintColumn and ConstraintRow.

Note

As of this writing, Flex Builder does not support ConstraintColumn, ConstraintRow, and their associated syntax in design view. Adobe plans to support this in a future release, but if you plan to use Flex Builder and you need design view, you may decide against using this feature.

To understand this better, let’s look at Example 6-5, which uses the additional constraints.

Example 6-5. Positioning children using constraint-based layout

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Canvas width="100%" height="100%">
         <mx:constraintColumns>
              <mx:ConstraintColumn id="column1" width="30%"/>
              <mx:ConstraintColumn id="column2" width="70%"/>
         </mx:constraintColumns>
        <mx:Button right="column1:10" label="Right of 1st Column"/>
        <!-- This will ignore the column constraints and will just use the main
parent Canvas -->
        <mx:Button horizontalCenter="0" label="Always centered"/>
        <!-- This makes use of the column constraint for the left but also uses the
main parent Canvas for the bottom -->
        <mx:Button left="column2:10" bottom="10" label="Left of 2nd Column"/>
    </mx:Canvas>
</mx:Application>

Figure 6-4 shows the results.

Results of

Figure 6-4. Results of Example 6-3

In Example 6-5, we defined two column constraints and positioned the children accordingly. The two columns in this example span the entire canvas, with the first column having a width of 30% and the second a width of 70%. In this example, we declared two ColumnConstraint instances and set their id property values to column1 and column2. We then referenced the constraints using these id values when setting the positioning properties (left, right, top, bottom, horizontalCenter, and verticalCenter) of the children. In every case, we need to use the syntax constraintID:value. For example, in the preceding code, we set the right property of the first button to column1:10, meaning that the button should always appear 10 pixels from the right edge of the column constraint with an id of column1.

The second button in Example 6-5 is centered horizontally, but unlike the first and third buttons, it does not make use of the new constraint regions. This might not seem obvious at first, but using a column or row constraint doesn’t prohibit you from still using the main canvas for positioning children. Thus, the second button will be positioned in relation to the entire canvas region and not within any of the columns in the example.

Finally, the third button in Example 6-5 is positioned 10 pixels from the bottom and left of the second column region. The second column has a width of 70%, and as no row constraint has been defined, the column spans the entire region vertically.

Here are some additional things to remember concerning constraint-based layouts:

  • Setting the top style property causes the child’s y value to be set to that many pixels away from the parent container’s top edge.

  • Setting the bottom style property causes the y property of the child to be set to the height of the container, minus the child’s height.

  • You can set both the bottom and top values of a child, thus resulting in the child height being resized automatically, while always satisfying the top and bottom constraints.

  • Setting the left style property sets the x value of the child at runtime to that many pixels away from the parent’s edge.

  • Setting the right style property at runtime sets the x value to the total width of the container minus the right value and the width of the child.

  • Setting both the right and left style properties causes the child to be resized to satisfy the constraint rules.

  • Setting the values top, bottom, left, and right causes the child to be resized and positioned to meet the conditions.

  • As with explicitly positioning items in a Canvas container, children you position using constraint-based layout rules can overlap each other.

  • You can set a child’s width and height to a percentage; the canvas positions and sizes them appropriately.

You can mix constraint-based layout with absolute positioning within the same Canvas container.

Hybrid layout containers

The containers Application, Panel, and TitleWindow are based on both box and canvas layout rules; that’s why we call them hybrid layout containers. The rules by which these children are laid out depend on the value of the container’s layout property. The layout property accepts three valid values: ContainerLayout.ABSOLUTE, ContainerLayout.HORIZONTAL, and ContainerLayout.VERTICAL.

When you set the layout property value to absolute the container behaves as a Canvas-based container. Setting the value to horizontal or vertical causes the container to act as a Box-based container with the appropriate direction (either a horizontal or a vertical layout, respectively).

Additional layout rules

Flex provides three other layout rules you can use in your application: Tile, Grid, and Form. These don’t serve a general purpose like the others do, and they're not shared across containers. Instead, they are embedded within specific containers, as discussed in the following sections.

The tile layout rule

The tile layout rule is found in the Tile container. Its purpose is to lay out children in a grid form, while optimally keeping the number of rows and columns equal. If it cannot keep them equal, it creates an extra row or column, depending on the direction property of the Tile container instance.

The direction property for the Tile container can accept two values: TileDirection.HORIZONTAL and TileDirection.VERTICAL. The default is for children to be laid out horizontally first (see Figure 6-5). This means that each child is instantiated one next to the other, horizontally, starting from left to right. When the intended number of children per row is reached depending on the optimal number of rows versus columns, it places the next child on the row below. Vertical orientation, of course, works the same way, except it begins laying children out next to each other from the top left and moving down until it reaches the current number of rows before continuing on to the next column.

Results from having a Tile container with seven children, using the default horizontal direction

Figure 6-5. Results from having a Tile container with seven children, using the default horizontal direction

The width and height properties, when set, play a key role in terms of how a Tile container lays out children. A Tile with a set width and height will be forced to satisfy those limits, thus causing the rule of rows to columns to be adjusted. Under such cases, the direction property will still behave the same.

The grid layout rule

The grid layout rule is used by the Grid container component. This layout rule/container replicates how an HTML table works in Flex, as shown in Example 6-6.

Example 6-6. Grid container example

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Grid>
        <mx:GridRow>
            <mx:GridItem width="100">
                <mx:Label text="Select a Color:"/>
            </mx:GridItem>
            <mx:GridItem>
                <mx:ColorPicker/>
            </mx:GridItem>
        </mx:GridRow>
        <mx:GridRow>
                <mx:GridItem colSpan="2" horizontalAlign="right">
                    <mx:Button label="Submit"/>
                </mx:GridItem>
        </mx:GridRow>
    </mx:Grid>
</mx:Application>

Example 6-6 contains two rows. The first row contains a Label and a ColorPicker. The second contains a Submit button, which is aligned to the right of the table. To understand this example, it is helpful to take a look at how Grid-related classes relate to traditional HTML tables:

  • An HTML <table> is synonymous with a Grid.

  • An HTML <tr> (table row) is synonymous with a GridRow.

  • An HTML <td> (table data) is synonymous with a GridItem.

  • The colspan and rowspan properties in HTML are properties of the GridItem as colSpan and rowSpan.

  • The HTML align attribute is a style called horizontalAlign.

  • The HTML valign property is a style called verticalAlign.

Note

If you are familiar with HTML, you may initially feel more comfortable using the Grid container over others.

Although the Grid container is a good representation of a familiar layout model, you should use the Grid container only as a last resort when laying out an application. Other containers such as Canvas and box-based containers are easier to maintain, provide better performance, and offer most of what Grid provides.

The form layout rule

The best way to introduce the form layout rule is to show an example of it in use (see Figure 6-6).

A typical form using the form layout rule

Figure 6-6. A typical form using the form layout rule

The form layout rule is found in the Form container. This container is used for laying out forms such as those you’d see on a web page, which typically include headings and input controls in a Flex application. The Form container, like the Grid container, has associated components and exists for convenience. You could reproduce the same layout using other containers, but for traditional forms you may find this container ideal.

The Form container’s related components are FormHeading and FormItem:

FormHeading

You use this to place a heading over a group of multiple FormItems within a Form by setting the label property. You can use multiple FormHeading controls within a form; you should place them before the group of form items the FormHeading represents. The label text is positioned and aligned to the body of the form items. You can control spacing between FormHeading children using the paddingTop, paddingLeft, and paddingRight style properties.

FormItem

You use this when a Form container needs to contain items such as input boxes and combo boxes. You can place multiple instances of FormItem within a single form. FormItem implements the box-based layout rule, which allows you to place multiple children within a FormItem and exposes a direction property in the same way that other box-based containers do. Finally, FormItem exposes a label property that allows you to place text to the left of a row in a form.

Example 6-7 shows the code you would use to reproduce Figure 6-7 using the Form container.

Example 6-7. Example of using the Form container

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Form>
        <mx:FormHeading label="Account Information"/>
        <mx:FormItem label="First Name, Last Name" direction="horizontal">
            <mx:TextInput id="firstName"/>
            <mx:TextInput id="lastName"/>
        </mx:FormItem>
        <mx:FormItem label="e-mail">
            <mx:TextInput id="email"/>
        </mx:FormItem>
        <mx:FormHeading label="Bug Report"/>
        <mx:FormItem label="Version">
            <mx:TextInput id="version"/>
        </mx:FormItem>
        <mx:FormItem label="Comment">
            <mx:TextArea id="comment" width="326" height="100"/>
        </mx:FormItem>
    </mx:Form>
</mx:Application>
Application output from

Figure 6-7. Application output from Example 6-7

Notice how the form neatly positions and sizes all the children. This is the convenience of using a Form container.

Padding, Borders, and Gaps

Thus far, we have worked with containers using many of their default behaviors. In this section, we will discuss the important issue of style properties for padding, borders, and gaps.

Padding, borders, and gaps are style properties that control how children are positioned. Padding controls the space between a child and the container’s border, and it is typically seen in box-, tile-, and grid-based layout containers. Borders are found in most containers, and they control the border surrounding the containers’ bounding box. Finally, gaps control the space between each child when working with box-, tile-, and grid-based layout containers. To better understand how these style properties work, look at Figure 6-8, which shows some of the style properties and their purposes.

Diagram of children within an HBox and corresponding padding, borders, and gaps

Figure 6-8. Diagram of children within an HBox and corresponding padding, borders, and gaps

It’s important when laying out applications to keep these style properties in mind, as these properties are taken into account when a container performs its measurement routines to determine how much space is available. Some containers, such as Application, for example, default to having 24-pixel padding on all four sides. If you wanted to lay out an application that would truly occupy 100% of the browser window, you would set the paddingTop, paddingBottom, paddingLeft, and paddingRight properties to 0. Let’s take a look at such an example:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" paddingBottom="0"
paddingLeft="0" paddingRight="0" paddingTop="0">
    <mx:Button label="Max sized button" width="100%" height="100%"/>
</mx:Application>

The preceding code will result in an application with one large button that truly occupies 100% of the available space in the browser, as shown in Figure 6-9 on the next page.

Application with zero padding

Figure 6-9. Application with zero padding

Nesting Containers

Most applications will require more than just one container to achieve the layout you desire. In such cases, you’ll want to use multiple containers, and perhaps mix Box- and Canvas-based containers in the same application. Flex allows you to easily nest containers within other containers, and although you may not have realized it earlier, you have been nesting containers all along, because Application is itself a container. When you nest containers, children of containers will act like any other children. Example 6-8 shows how to nest containers; Figure 6-10 shows the results.

Example 6-8. Example of nesting containers

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:HBox width="100%" height="100%">
        <mx:Canvas width="50%" height="100%">
            <mx:Button label="Button 1" bottom="10" right="10"/>
            <mx:Button label="Button 2" bottom="40" right="10"/>
        </mx:Canvas>
        <mx:Panel width="50%" height="100%" layout="absolute">
        </mx:Panel>
    </mx:HBox>
</mx:Application>
Results of

Figure 6-10. Results of Example 6-8

Example 6-8 combines multiple container types to achieve a layout that you could not easily attain with just one container. In this example, two areas in the application have been separated. To do this, we used an HBox container and placed two containers, one Panel and one Canvas, within, each occupying 50% of the total area. In this example, HBox is managing the positioning and layout of the two subcontainers. Also, each child container has its own area where the children rely on its layout rules and not on those of HBox.

Although this is a simple example, nested containers provide a powerful and easy way to lay out complex Flex applications. It is good to keep in mind, however, that excessive container nesting can lead to poor performance, and generating the layout you desire using constraint- or canvas-based layout may lead to a better overall application experience for the user.

Nested containers are ideal in another scenario as well. At times, you may want to use a container because of its chrome appearance. For example, a Panel container does not implement the form layout rule, but it does provide a chrome appearance that you might be interested in using. For such a case, you can nest a Form container (or any other container) within a Panel to obtain the chrome appearance of the Panel and the layout rules of the nested container.

Handling Scrolling and Clipping

Containers can’t always fit their children within the available viewing area. This can occur because, for example, screen resolutions are different among end-users, the children need more space than what’s available, or the canvas was resized. As a result, sooner or later you will have to deal with containers that have a scroll bar. Thankfully, Flex makes the process of dealing with this problem simple.

If a container doesn’t have enough available space, a scroll bar (shown in Figure 6-11) appears by default, which allows the user to reach the content he desires.

The scroll bar that appears when clipping occurs by default in containers

Figure 6-11. The scroll bar that appears when clipping occurs by default in containers

You can override this default behavior and have a container always display a scroll bar, or none at all even if one is needed. Each container has the properties horizontalScrollPolicy and verticalScrollPolicy. These properties accept the values ScrollPolicy.ON, ScrollPolicy.OFF, and ScrollPolicy.AUTO, the latter which is the default value for containers.

Another functionality that containers perform on children when they exceed the available area is clipping. Clipping is the process whereby a container will clip (hide) the parts that exceed its viewable area. In the preceding example, the content was clipped, but a scroll bar was provided to allow the user to scroll. Flex containers allow you to disable clipping, if needed, using the clipContent property. By default, the clipContent property is set to true; if it is set to false, the content will not be clipped. Disabling clipping will also cause the children to extend beyond the container’s boundaries. This can have adverse effects on your application layout, as the container boundaries are not adhered to when clipping is disabled.

The Spacer Component

The Spacer component is a component that can assist in handling layout within Flex. It is commonly used when you need to reposition a child when you’re using a container that does not give you precise control, such as the Canvas container. The Spacer component is treated like any other container child component. You can add it to a container and give it width and height. Once you add it to a component, it does not render to the user’s screen. Instead, it will just occupy the space a regular component would occupy.

Figure 6-12 shows a simple application in which three buttons are displayed. The buttons are placed within an HBox component, and the first and second buttons, unlike the second and third, require extra space in between. To achieve this, a Spacer component is used in between the first and second buttons.

Three buttons within an HBox, with a Spacer component separating the first and second buttons

Figure 6-12. Three buttons within an HBox, with a Spacer component separating the first and second buttons

Here is the code used to create Figure 6-12:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:HBox>
        <mx:Button label="First"/>
        <mx:Spacer width="40"/>
        <mx:Button label="Second"/>
        <mx:Button label="Third"/>
    </mx:HBox>
</mx:Application>

Spacers make it easy to adjust child positioning rules. Although you may be tempted to use Spacers everywhere, it is recommended that you first review why you need Spacers instead of assuming that Spacers may be the ideal solution, because often you may find that your layout can be handled in a more ideal manner.

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

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