Using constraints to control component layout

You can manage a child component’s size and position simultaneously by using constraint-based layout, or by using constraint rows and columns. Constraint-based layout lets you anchor the sides or center of a component to positions relative to the viewable region of the component’s container. The viewable region is the part of the component that is being displayed, and it can contain child controls, text, images, or other contents.

Constraint rows and columns let you subdivide a container into vertical and horizontal constraint regions to control the size and positioning of child components with respect to each other and within the parent container.

Creating a constraint-based layout

You can use constraint-based layout to determine the position and size of the immediate children of any container that supports absolute positioning. With constraint-based layout, you can do the following:

  • Anchor one or more edges of a component at a pixel offset from the corresponding edge of its container’s viewable region. The anchored child edge stays at the same distance from the parent edge when the container resizes. If you anchor both edges in a dimension, such as top and bottom, the component resizes if the container resizes.

  • Anchor the child’s horizontal or vertical center (or both) at a pixel offset from the center of the container’s viewable region. The child does not resize in the specified dimension unless you also use percentage-based sizing.

  • Anchor the baseline of a component at a pixel offset from the top edge of its parent container.

You can specify a constraint-based layout for any Flex framework component (that is, any component that extends the UIComponent class). The following rules specify how to position and size components by using constraint-based layout:

  • Place the component in any Spark container that uses BasicLayout, in a MX Canvas container, or in a MX Application or MX Panel container with the layout property set to absolute.

  • Specify the constraints by using the baseline, top, bottom, left, right, horizontalCenter, or verticalCenter properties of UIComponent and GraphicElement.
    Note: In previous releases of Flex, the baseline, top, bottom, left, right, horizontalCenter, or verticalCenter properties were implemented as styles. You can still use them as styles in this release.

    The top, bottom, left, and right properties specify the distances between the component sides and the corresponding container sides.

    The baseline constraint specifies the distance between the baseline position of a component and the upper edge of its parent container. Every component calculates its baseline position as the y-coordinate of the baseline of the first line of text of the component. The baseline of a UIComponent object that does not contain any text is calculated as if the UIComponent object contained a UITextField object that uses the component's styles, and the top of the UITextField object coincides with the component's top.

    The horizontalCenter and verticalCenter properties specify distance between the component’s center point and the container’s center, in the specified direction; a negative number moves the component left or up from the center.

    The following example anchors the Form control’s left and right sides 20 pixels from its container’s sides:

    <mx:Form id="myForm" left="20" right="20"/>
  • Do not specify a top or bottom property with a verticalCenter property; the verticalCenter value overrides the other properties. Similarly, do not specify a left or right property with a horizontalCenter property.

  • A size determined by constraint-based layout overrides any explicit or percentage-based size specifications. If you specify left and right constraints, for example, the resulting constraint-based width overrides any width set by a width or percentWidth property.

Precedence rules for constraint-based components

  • If you specify a single edge constraint (left, right, top, or bottom) without any other sizing or positioning parameter, the component size is the default size and its position is determined by the constraint value. If you specify a size parameter (width or height), the size is determined by that parameter.

  • If you specify a pair of constraints (left-right or top-bottom), the size and position of the component is determined by those constraint values. If you also specify a center constraint (horizontalCenter or verticalCenter), the size of the component is calculated from the edge constraints and its position is determined by the center constraint value.

  • Component size determined by a pair of constraint-based layout properties (left-right or top-bottom) overrides any explicit or percentage-based size specifications. For example, if you specify both left and right constraints, the calculated constraint-based width overrides the width set by a width or percentWidth property.

  • Edge constraints override baseline constraints.

Example: Using constraint-based layout for a form

The following example code shows how you can use constraint-based layout for a form. In this example, the Form control uses a constraint-based layout to position its top just inside the canvas padding. The form left and right edges are 20 pixels from the outer SkinnableContainer container’s left and right edges. The second, inner, SkinnableContainer that contains the buttons uses a constraint-based layout to place itself 20 pixels from the right edge and 10 pixels from the bottom edge of the outer SkinnableContainer container.

If you change the size of your browser, stand-alone Flash Player, or AIR application, you can see the effects of dynamically resizing the application container on the Form layout. The form and the buttons overlap as the application grows smaller, for example.

<?xml version="1.0"?>
<!-- components\ConstraintLayout.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

    <s:SkinnableContainer width="100%" height="100%">
         <!-- Anchor the top of the form at the top of the canvas.
            Anchor the form sides 20 pixels from the canvas sides. -->
        <s:Form id="myForm" 
                backgroundColor="#DDDDDD"
                top="0" 
                left="20" 
                right="20">
                
            <s:FormItem label="Product:" width="100%">
                <!-- Specify a fixed width to keep the ComboBox control from 
                    resizing as you change the application size. -->
                <s:ComboBox width="200"/>
            </s:FormItem>

            <s:FormItem label="User" width="100%">
                <s:ComboBox width="200"/>
            </s:FormItem>

            <s:FormItem label="Date">
                <mx:DateField/>
            </s:FormItem>

            <s:FormItem width="100%" 
                    label="Hours:">
                <s:TextInput width="75"/>
            </s:FormItem>

            <s:FormItem width="100%" 
                    label="Minutes:">
                <s:TextInput width="75"/>
            </s:FormItem>
        </s:Form>

        <!-- Anchor the box with the buttons 20 pixels from the canvas
            right edge and 10 pixels from the bottom. -->
        <s:SkinnableContainer id="okCancelBox" 
                right="20" 
                bottom="10">
            <s:layout>
                <s:HorizontalLayout/>
            </s:layout>
            <s:Button label="OK"/>
            <s:Button label="Cancel"/>
        </s:SkinnableContainer>
    </s:SkinnableContainer>
</s:Application>

The executing SWF file for the previous example is shown below:

Using constraint rows and columns with MX containers and the Spark FormLayout class

You can subdivide a container that supports absolute positioning into vertical and horizontal constraint regions to control the size and positioning of child components with respect to each other, or with respect to the parent container.

Note: Constraint rows and columns are only supported by the MX containers, and the Spark FormLayout class.

You define the horizontal and vertical constraint regions of a container by using the constraintRows and constraintColumns properties. These properties contain Arrays of constraint objects that partition the container horizontally (ConstraintColumn objects) and vertically (ConstraintRow objects). ConstraintRow objects are laid out in the order they are defined, from top to bottom in their container; ConstraintColumn objects are laid out from left to right in the order they are defined.

The following example shows a Canvas container partitioned into two vertical regions and two horizontal regions. The first constraint column occupies 212 pixels from the leftmost edge of the Canvas. The second constraint column occupies 100% of the remaining Canvas width. The rows in this example occupy 80% and 20% of the Canvas container’s height from top to bottom, respectively.

<?xml version="1.0"?>
<!-- constraints\BasicRowColumn.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>
    <mx:Canvas>
        <mx:constraintColumns>
            <mx:ConstraintColumn id="col1" width="212"/>
            <mx:ConstraintColumn id="col2" width="100%"/>
        </mx:constraintColumns>
        <mx:constraintRows>
            <mx:ConstraintRow id="row1" height="80%"/>
            <mx:ConstraintRow id="row2" height="20%"/>
        </mx:constraintRows>

    <!-- Position child components based on 
        the constraint columns and rows. -->
    </mx:Canvas>    
</s:Application>

The executing SWF file for the previous example is shown below:

Constraint rows and columns do not have to occupy 100% of the available area in a container. The following example shows a single constraint column that occupies 20% of the Canvas width; 80% of the container is unallocated:

<?xml version="1.0"?>
<!-- constraints\BasicColumn_20Percent.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>
    <mx:Canvas>
        <mx:constraintColumns>
            <mx:ConstraintColumn id="col1" width="20%"/>
        </mx:constraintColumns>
    </mx:Canvas>

    <!-- Position child components based on 
        the constraint column. -->

</s:Application>

Creating constraint rows and columns

Constraint columns and rows have three sizing options: fixed, percent, and content. These options dictate the amount of space that the constraint region occupies in the container. As child components are added to or removed from the parent container, the space allocated to each ConstraintColumn and ConstraintRow instance is computed according to its sizing option.

  • Fixed size means the space allocated to the constraint region is a fixed pixel size. In the following example, you set the fixed with of a ConstraintColumn instance to 100 pixels:

    <mx:ConstraintColumn id="col1" width="100"/> 

    As the parent container grows or shrinks, the ConstraintColumn instance remains 100 pixels wide.

  • Percent size means that the space allocated to the constraint row or column is calculated as a percentage of the space remaining in the parent container after the space allocated to fixed and content size child objects has been deducted from the available space.

    In the following example, you set the width of a ConstraintColumn instance to 80%:

    <mx:ConstraintColumn  id="col1" width="80%"/> 

    As the parent container grows or shrinks, the ConstraintColumn always takes up 80% of the available width.

    A best practice in specifying percent constraints is to ensure that the sum of all percent constraints is less than or equal to 100%. However, if the total value of percent specifications is greater than 100%, the actual allocated percentages are calculated so that the proportional values for all constraints total 100%. For example, if the percentages for two constraint objects are specified as 100% and 50%, the values are adjusted to 66.6% and 33.3% (two-thirds for the first value and one-third for the second).

  • Content size (default) means that the space allocated to the region is dictated by the size of the child objects in that space. As the size of the content changes, so does the size of the region. Content sizing is the default when you do not specify either fixed or percentage sizing parameters.

    In the following example, you specify content size by omitting any explicit width setting:

    <mx:ConstraintColumn  id="col1"/> 

    The width of this ConstraintColumn is determined by the width of its largest child. When children span multiple content sized constraint rows or constraint columns, Flex divides the space consumed by the children among the rows and columns.

For the ConstraintColumn class, you can also use the maxWidth and minWidth properties to limit the width of the column. For the ConstraintRow class, you can use the maxHeight and minHeight properties to limit the height of the row. Minimum and maximum sizes for constraint columns and rows limit how much the constraint regions grow or shrink when you resize their parent containers. If the parent container with a constraint region shrinks to less than the minimum size for that region when you resize the container, scroll bars appear to show clipped content.

Note: Minimum and maximum limits are only applicable to percentage and content sized constraint regions. For fixed size constraint regions, minimum and maximum values, if specified, are ignored.

Positioning child components based on constraint rows and constraint columns

Anchor a child component to a constraint row or constraint column by prepending the constraint region’s ID to any of the child’s constraint parameters. For example, if the ID of a ConstraintColumn is "col1", you can specify a set of child constraints as left="col1:10", right="col1:30", horizontalCenter="col1:0".

If you do not qualify constraint parameters (left, right, top, and bottom) a constraint region ID, the component is constrained relative to the edges of its parent container. Components can occupy a single constraint region (row or column) or can span multiple regions.

The following example uses constraint rows and constraint columns to position three Button controls in a Canvas container:

<?xml version="1.0"?>
<!-- constraints\ConstrainButtons.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

    <mx:Canvas id="myCanvas" backgroundColor="0x6699FF">
        <mx:constraintColumns>
            <mx:ConstraintColumn id="col1" width="100"/>
            <mx:ConstraintColumn id="col2" width="100"/>
        </mx:constraintColumns>
        <mx:constraintRows>
            <mx:ConstraintRow id="row1" height="100"/>
            <mx:ConstraintRow id="row2" height="100"/>
        </mx:constraintRows>

        <s:Button label="Button 1" 
            top="row1:10" bottom="row1:10" 
            left="10"/>
        <s:Button label="Button 2" 
            left="col2:10" right="col2:10"/>
        <s:Button label="Button 3" 
            top="row2:10" bottom="row2:10" 
            left="col1:10" right="10"/>
    </mx:Canvas>

    <s:Label text="canvas width:{myCanvas.width}"/>
    <s:Label text="canvas height:{myCanvas.height}"/>
</s:Application>

The executing SWF file for the previous example is shown below:

The next example defines the constraint rows and columns by using percentages. As you resize the application, the Button controls resize accordingly.

<?xml version="1.0"?>
<!-- constraints\ConstrainButtonsPercent.mxml -->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:mx="library://ns.adobe.com/flex/mx" 
    xmlns:s="library://ns.adobe.com/flex/spark">
    <s:layout>
        <s:VerticalLayout/>
    </s:layout>

    <mx:Canvas id="myCanvas"
        backgroundColor="0x6699FF" 
        width="100%" height="100%">

        <mx:constraintColumns>
            <mx:ConstraintColumn id="col1" width="30%"/>
            <mx:ConstraintColumn id="col2" width="40%"/>
            <mx:ConstraintColumn id="col3" width="30%"/>
        </mx:constraintColumns>
        <mx:constraintRows>
            <mx:ConstraintRow id="row1" height="35%"/>
            <mx:ConstraintRow id="row2" height="55%"/>
        </mx:constraintRows>

        <s:Button label="Button 1" 
            top="row1:10" bottom="row2:10" 
            left="10"/>
        <s:Button label="Button 2" 
            left="col2:10" right="col3:10"/>
        <s:Button label="Button 3" 
            top="row2:10" bottom="row2:10" 
            left="col3:10" right="col3:10"/>
    </mx:Canvas>

    <s:Label text="canvas width:{myCanvas.width}"/>
    <s:Label text="canvas height:{myCanvas.height}"/>    
</s:Application>

The executing SWF file for the previous example is shown below:

While you can specify any combination of qualified and unqualified constraints, some constraint properties may be overridden. The priority of sizing and positioning constraints are as follows:

  1. Center constraint specifications override all other constraint values when determining the position of a control.

  2. Next, left edge and top positions are determined.

  3. Finally, right edge and bottom positions are calculated to best fit the component.

The following table defines the behavior of constrained components when they are contained in a single constraint region. Edge 1 is the first specified edge constraint for a child component (left, right, top, or bottom). Edge 2 is the second specified edge constraint of a pair (left and right, top and bottom). Size is an explicit size for a child component (width, height). Center is the positioning constraint to center the child object (horizontalCenter or verticalCenter).

Constraint Parameters

 

 

 

Behavior

 

Edge 1

Edge 2

Size

Center

Size

Position

x

 

 

 

Default component size

Relative to specified edge constraint

x

x

 

 

Calculated from specified edge constraints

Relative to specified edge constraints

x

x

x

 

Determined from specified edge constraints. Explicit size is overridden

Relative to specified edge constraints

x

 

x

 

Specified size

Relative to specified edge

x

 

 

x

Default component size

Centered in constraint region; edge constraint is ignored

x

x

 

x

Calculated from edge constraints

Centered in constraint region

x

 

x

x

Explicit size

Centered in constraint region; single edge constraint is ignored

 

 

 

x

Default size of component

Centered in constraint region

Using Baseline property

The baseline property defines the offset between the baseline defined by the constraint row (or the top of the container) and the baseline position of the element.

The baseline property can be specified as a number or can be specified with respect to the max ascent of the row.

If the baseline value on a row is only a number, the elements contained by the row are positioned that many pixels below the top of the container. If the baseline value is defined with respect to the max ascent of the row, then it is defined in the format: "maxAscent:x". The default value is "maxAscent:0".

The following code example defines four labels in a group, where each label is constrained with a baseline alignment to row1 and maxAscent:5.

<?xml version="1.0" encoding="utf-8"?>
<!-- containers\layouts\spark\SparkFormItemLayoutBaselineAlignment.mxml --> 
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
               xmlns:mx="library://ns.adobe.com/flex/mx"
               xmlns:s="library://ns.adobe.com/flex/spark">
    
    <s:Group y="200" x="30">
        <s:layout>
            <s:FormItemLayout>
                <s:constraintRows>
                    <s:ConstraintRow id="row1" />
                    <s:ConstraintRow id="row2" baseline="maxAscent:5"/>
                    <s:ConstraintRow id="row3" baseline="30"/>
                </s:constraintRows>
            </s:FormItemLayout>
        </s:layout> 
        <s:Label fontSize="20" text="hello!" baseline="row1:10" x="38" color="blue"/>
        <s:Label fontSize="25" text="hello!" baseline="row1:0" x="89" color="#FF9933"/>
        <s:Label fontSize="35" text="hello!" baseline="row1:0" x="149" color="green"/>
        <s:Label fontSize="50" text="hello!" baseline="row1:0" x="234" />
    </s:Group>
</s:Application>

The executing SWF file for the previous example is shown below: