A number of basic layouts, such as box, tile and grid, are available to a Flex developer for designing application UI. While the box layout is arguably the most used of these, another one Iâve seen used quite often is the canvas. The Canvas container gives the developer total control over the positioning of its children.
In the HTML world, it is the CSS stylesheet that typically dictates the layout: the HTML document simply declares the various top-level containers along with their IDs or CSS class names, while an associated CSS file sets up the layout by specifying appropriate values for the left, the top, the width, the height and the display styles for each container.
Unfortunately, Flex does not support the controlling of layout in this manner using an external CSS stylesheet. x, y, width and height are properties, not styles, of the UIObject class.
Thankfully the designers of Flex made it flexible enough for smart component developers to be able to build such support into their own components by writing a few lines of ActionScript. To illustrate, letâs create a new container called Div, which, like the HTML div element, supports the layout-related CSS styles mentioned in previous paragraphs.
First we need to decide which container to base our Div upon. Since we need absolute positioning but also want some automatic measurement and layout to fall back upon, letâs use the Canvas as our base.
Weâll have to override two methods from the Canvas: measure and layoutChildren.
In measure, we set the preferred width and the preferred height to the values specified by the width and the height styles. If no values are specified for these styles, the default behaviour is to use the measurement done by the Canvas. Also, if the display style is set to a value of ânoneâ, we set the preferred width and height to 0.
public function measure():Void
{
super.measure();
var w:Number = getNonInheritingStyle("width");
var h:Number = getNonInheritingStyle("height");
// 'width' and 'height' specified in CSS override the calculated
// preferred width and height.
if (w != undefined)
_measuredPreferredWidth = w;
if (h != undefined)
_measuredPreferredHeight = h;
var display:String = getNonInheritingStyle("display");
// If 'display' is set to "none", set preferred width and height to 0.
if (display == "none")
{
_measuredPreferredWidth = 0;
_measuredPreferredHeight = 0;
}
}
In layoutChildren, we position each child as per its left and top styles. If no values are specified, again, we fall back upon the Canvasâs layout (which is to leave the child alone and let it go by its x and y).
public function layoutChildren():Void
{
super.layoutChildren();
var n:Number = numChildren;
for (var i:Number = 0; i < n; i++)
{
var child:UIObject = getChildAt(i);
// Respect 'left' and 'top' styles of child.
var left:Number = child.getNonInheritingStyle("left");
var top:Number = child.getNonInheritingStyle("top");
if (left != undefined)
child.x = left;
if (top != undefined)
child.y = top;
// If 'display' is "none", hide child; else show it.
if (child.getNonInheritingStyle("display") == "none")
child.visible = false;
else
child.visible = true;
}
}
Hereâs what a sample two-column layout in MXML looks like: