How to use the AS3-Layout-Framework

The AS3-Layout-Framework is basically very simple and easy to understand. It is based on one structural (Composite) and one behavioural (Strategy) pattern.

The composite pattern is used to compose objects into tree structures to represent part-whole hierarchies. Also it has the advantage to let clients treat individual objects and compositions of objects uniformly[GHJV95]. While updating all ILayoutComponents, a ILayoutContainer ( extends ILayoutComponent) lays out its subcomponents and a simple ILayoutComponent just handles its bounds ( Figure 1 shows the inheritance structure of the participants in more detail ).

The strategy pattern is used to encapsulate algorithms in objects with a well formed interface and lets the algorithm vary independently from clients that use it[GHJV95].
In the AS3-Layout-Framework the ILayout embodied the strategy pattern. This makes the creation of concrete layouts very simple and easy to plug in (f.e. GridLayout, FlowLayout).

Display representation:

The AS3-Layout-Framework itself is loosely-coupled from the display list. But to ease the use of it in daily usage, UILayoutComponent extends Sprite and according to that UILayoutComponent and UILayoutContainer are binded to the display list ( see the inheritance structure in Figure 1 ).

To layout an existing project all visible representations just have to extend UILayoutComponent or UILayoutContainer.

In addition ILayoutComponent extends ILayoutObservable and provides the functionality to add ILayoutObservers. Which means that other objects can register themself for specific layout events.

NOTE: The fact that ECMA-Script does not support multiple inheritance led to simplify the display inheritance structure. Otherwise the existing code had to be copied in every new concrete implementation (Bitmap, Shape, Sprite, MovieClip...). This would end up in error prone and unmaintainable code.

Worth knowing:

The internal data structure of the AS3-Layout-Framework is very similar to the data structure of the IList interface in the AS3-Collection-Framework.

This means that the add, remove and set methods work nearly the same way. There is just one difference: there are no null s allowed. After adding a ILayoutComponent at a specified index bigger than the component amount, the lag will be filled with NullComponent s. This has one big advantage. Developers can customize there own ILayoutStrategy without dealing with lags in calculation.

The following section covers two fundamental use cases of the layout's data structure, which is especially for those readers who are not familiar with the AS3-Collection-Framework.

addComponentAt( index: int, c: ILayoutComponent ): void

    adds a ILayoutComponent at a specified index, if the index is bigger than the component amount, the lag will be filled with NullComponent s (list does not contain null s), otherwise the element currently at that position and any subsequent elements will be shifted to the right and the given ILayoutComponent will be inserted at the specified index.
    NOTE: same as splice( index, 0, object ) in the Array class.

setComponentAt( index: int, c: ILayoutComponent ): void

    sets a ILayoutComponent at a specified index, if the index is bigger than the component amount, the same logic of addComponentAt( index: int, c: ILayoutComponent ) takes place. Otherwise the ILayoutComponent at the specified index will be replaced with the given ILayoutComponent.
    NOTE: same as splice( index, 1, object ) in the Array class

Example:

SimpleLayoutExample.as

package
{
	import com.addicted2flash.layout.Column;
	import com.addicted2flash.layout.FlowLayout;
	import com.addicted2flash.layout.GridLayout;
	import com.addicted2flash.layout.ILayoutStrategy;
	import com.addicted2flash.layout.LayoutMatrix;
	import com.addicted2flash.layout.LayoutSettings;
	import com.addicted2flash.layout.Padding;
	import com.addicted2flash.layout.Row;
	import com.addicted2flash.layout.Size;
	import com.addicted2flash.layout.Style;
	import com.addicted2flash.layout.UILayoutContainer;
	import com.addicted2flash.layout.example.SimpleComponent;
 
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.Rectangle;	
 
	/**
	 * @author Tim Richter
	 */
	public class SimpleLayoutExample extends Sprite
	{
		protected var _container: UILayoutContainer;
 
		/**
		 * Create a new <code>SimpleLayoutExample</code>.
		 */
		public function SimpleLayoutExample()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			stage.addEventListener( Event.RESIZE, onResize );
 
			_container = new UILayoutContainer( new LayoutSettings( 0, 0, new Size( 800, 600 ) ) );
			addChild( _container );
 
//			_container.layout = createFlowLayout();
			_container.layout = createGridLayout();
 
			_container.padding = new Padding( 10, 5, 25, 50 );
 
			_container.addComponent( new SimpleComponent( new LayoutSettings( .5, .5, new Size( 100, 80 ) ) ) );
			_container.addComponent( new SimpleComponent( new LayoutSettings( 0, 1, new Size( 140, 100 ) ) ) );
			_container.addComponent( new SimpleComponent( ) );
 
			_container.addComponent( new SimpleComponent( ) );
			_container.addComponent( new SimpleComponent( ) );
 
			update( );
		}
 
		/**
		 * @inheritDoc
		 */
		protected function update(): void
		{
			_container.setLayoutSize( stage.stageWidth, stage.stageHeight );
		}
 
		protected function createFlowLayout(): ILayoutStrategy
		{
			return new FlowLayout( 0, 0, Style.BOTTOM_RIGHT );
		}
 
		protected function createGridLayout(): ILayoutStrategy
		{
			return new GridLayout( 3, 4, 10, 20, Style.RIGHT_TO_LEFT );
		}
 
		private function onResize( event: Event ): void
		{
			update();
		}
	}
}

SimpleComponent.as

 
package
{
	import com.addicted2flash.layout.ILayoutComponent;
	import com.addicted2flash.layout.LayoutEventType;
	import com.addicted2flash.layout.LayoutSettings;
	import com.addicted2flash.layout.UILayoutComponent;
 
	import flash.geom.Rectangle;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;		
 
	/**
	 * This class represents a basic <code>UILayoutComponent</code> that draws an outline around
	 * the layout bounds.
	 *
	 * @author Tim Richter
	 */
	public class SimpleComponent extends UILayoutComponent
	{
		private static var _count: int = 0;
		private var _tField: TextField;
 
		/**
		 * Create a new <code>SimpleComponent</code>.
		 *
		 * @param settings <code>LayoutSettings</code>
		 */
		public function SimpleComponent( settings: LayoutSettings = null )
		{
			super( settings );
 
			initialize( );
		}
 
		/**
		 * @inheritDoc
		 */
		override public function processLayoutEvent( type: int, c: ILayoutComponent ): void
		{
			if( type & LayoutEventType.BOUNDS )
			{
				drawOutline( layoutBounds );
 
				updateTextField( );
			}
		}
 
		private function updateTextField(): void
		{
			_tField.x = ( layoutBounds.width - _tField.textWidth ) / 2;
			_tField.y = ( layoutBounds.height - _tField.textHeight ) / 2;
		}
 
		private function drawOutline( bounds: Rectangle ): void
		{
			graphics.clear( );
			graphics.lineStyle( 1, 0xcecece );
			graphics.beginFill( 0xdddddd, .4 );
			graphics.drawRect( 0, 0, bounds.width, bounds.height );
		}
 
		private function initialize(): void
		{
			_tField = new TextField( );
			_tField.textColor = 0x666666;
			_tField.autoSize = TextFieldAutoSize.LEFT;
			_tField.selectable = false;
			_tField.text = ( _count++ ).toString();
 
			addChild( _tField );
		}
	}
}
 

Layout Framework Inheritance
Figure 1 : Inheritance structure of AS3-Layout-Framework

References:

[GHJV95] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns – Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995

AS3-Layout-Framework example

11 Responses (Add Your Comment)

  1. Hi! I have just come across your layout framework! It works wonders! The example code is a bit confusing because the LEFT_TO_RIGHT and TOP_TO_BOTTOM lines cause errors but I just removed these!
    However, I was just wondering how I might go about setting minimum width and height for the container. What I mean is that if the stage size is less than a given height or width then the layout should not resize.
    Also, is it possible to update the layout at run time: i.e. to add or remove columns or rows from a configuration panel I am considering to create.
    And is it possible to create columns and rows inside an existing column or row?

    I know I am asking a lot of questions but if you have time to answer them I will be very grateful and will gladly share any modifications I might have made for a layout configuration at run time :D

  2. Hi Alex,

    do you have the latest repository version? There are parameters in the GridLayout that have to be setted (f.e. row, column) otherwise errors will be thrown.
    The horizontal and vertical alignment of the GridLayout is just a feature. The default is setted to LEFT_TO_RIGHT, TOP_TO_BOTTOM. To my mind this is easy to understand, because the naming really describes the functionality.

    When you take a look at the documentation of the specific layout implementation you will find the possiblities to manipulate the layout at runtime. F.e. the GridLayout’s columns and rows can be manipulated at runtime by a getter/setter.

    It does not make sence not to resize the container if the size of the stage is less than the minimum size of the container. This must work in this way. When you think about the workflow once again, you definitely will agree.

    It is very important to understand the structure of such a layout framework. The AS3-Layout-Framework (the same in JAVA) decouples the layout algorithms from the internal representation of the containers. The layout provides a template how the visual ordering should look like. In the container, you can manipulate the order of the subcomponents.

  3. Thanks for your reply… I look forward to looking furthur into the framework and sharing my results with you.

    I noticed that you mentioned that it doesn’t make sense not to resize the container if the stage is less than the minimum size of the container. I think I understand what you mean concerning the workflow and I guess I will have to agree. What I was referring to was this:

    With an HTML div layout you can create a container div with child divs in it which resize according to the browser “stage” but when the browser is smaller than the minimum height or width (set in CSS) of that parent container… the container no longer resizes.

    For the moment I’ll make do :D Thanks for the great framework.

  4. Hi Alex,

    thank you for your response.

    But this works the same way in the AS3-Layout-Framework.

    The AS3-Layout-Framework is not designed in a bottom-to-top structure. It is layouting in a top-to-bottom manner with the possibility to retrieve crop events. This means that a ILayoutComponent’s cropped size will be setted whenever the layout bounds are smaller than the minimum size.

    This workflow is more dynamic and all ILayoutComponents can handle the cropped size individually, f.e. enable scrolling.

  5. Hello,

    In the current example, and also in the GridLayout and the FlowLayout examples you write:

    _container = new UILayoutContainer(…
    // …
    _container.updateLayoutComponent(…

    But I can’t find any updateLayoutComponent method in the UILayoutContainer class.

    So I always have errors when I compile your examples.

    I browse an old revision of this class (16 nov revision here: http://code.google.com/p/addicted2flash/source/browse/trunk/src/com/addicted2flash/layout/UILayoutComponent.as?r=205) and I can’t find this method.

    The SimpleComponent.as fire also a compilation error : “1020: Method marked override must override another method.”

    I would love to test your framework because it seems that yours is the most developed that can be found in opensource. It would be great if you could post your examples with sources (.as + FLA) ready to compile.

  6. Hi Frank,

    I’m really sorry about that. I changed the naming of the methods and updated the internal update procedure. Unfortunately I missed to update the examples as well. The examples are updated now.

    Now every update is dispatched to one method (processLayoutEvent(...)). Every UILayoutComponent implements ILayoutObserver and adds to its internal list. This makes the update of custom LayoutComponents very easy. You can override the get acceptedLayoutEvents() method to subscribe for specific events.

  7. Hello again,

    I try the new files and it works. Thank you!

    Just to note, the as code on the page is still wrong :)

    I try to compile the GridLayout example (http://www.addicted2flash.com/2008/11/the-gridlayout/) and the FlowLayout example (http://www.addicted2flash.com/2008/11/the-flowlayout/) and I still have multiple errors on each. Can you correct those too please ?

  8. Hello again,
    I just trying to use the LayoutDebugger class.

    Just to note, the example at the top of the asdoc file (http://www.addicted2flash.com/api/com/addicted2flash/layout/LayoutDebugger.html or http://code.google.com/p/addicted2flash/source/browse/trunk/src/com/addicted2flash/layout/LayoutDebugger.as) is wrong.

    LayoutDebugger.initialize can’t take LayoutDebuggerSettings as parameter. And it’s missing some “)” brackets.

  9. Hi Frank,

    you have to use the newest revision of the library. You probably have an older revision and try to use updated examples.

  10. Yes if works now. All 4 examples (flow, grid,…) Thanks! :)

    About the Debbuger. It seems impossible to render the Row or the Column with the debbuger. Can you confirm it ?

  11. Hi Frank,

    that’s right. The Debugger just draws an outline around the layout bounds of all components in the container.

    The Matrix is just a data-structure. Its physical representation (rows, columns) dont’t have to be of the same size as the components (see preferred size of the components).

Leave a Reply

Formatting: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>