Tuesday, May 26, 2009

Something hidden about the Container

Although at the level of a Flash DisplayObjectContainer, all
children are equal, in a Flex Container some children are "more
equal than others". (George Orwell, "Animal Farm")

In particular, Flex distinguishes between content children and
non-content (or "chrome") children. Content children are the kind
that can be specified in MXML. If you put several controls
into a VBox, those are its content children. Non-content children
are the other ones that you get automatically, such as a
background/border, scrollbars, the titlebar of a Panel,
AccordionHeaders, etc.

Most application developers are uninterested in non-content children,
so Container overrides APIs such as numChildren and getChildAt()
to deal only with content children. For example, Container, keeps
its own _numChildren counter.

Container assumes that content children are contiguous, and that
non-content children come before or after the content children.
In order words, Container partitions DisplayObjectContainer's
index range into three parts:

A B C D E F G H I
0 1 2 3 4 5 6 7 8 <- index for all children
0 1 2 3 <- index for content children

The content partition contains the content children D E F G.
The pre-content partition contains the non-content children
A B C that always stay before the content children.
The post-content partition contains the non-content children
H I that always stay after the content children.

Container maintains two state variables, _firstChildIndex
and _numChildren, which keep track of the partitioning.
In this example, _firstChildIndex would be 3 and _numChildren
would be 4.

Thursday, May 7, 2009

Flex Layout Techniques - Part I

From now on I am going add some Flex Layout techniques, with code samples.

1) If you have HBox of some width and wanted to align two controls at each end.

<mx:HBox width="500">
<mx:Button label="Left Button"/>
<mx:Spacer width="100%"/>
<mx:Button label="Right Button"/>
</mx:HBox>


This will align the first button to the left and the second button to the right. There's nothing special about the Spacer, it just extends the UIComponent. It's just a coding trick.

2) If you have Canvas of width and height, how would you align a control to the bottom-right corner. Ofcourse, you can do so by setting the x and y values, which is not the best practice.
If the canvas width and height is dynamic, the x and y values could fail.


<mx:Canvas width="500" height="800">
<mx:Image source="someImage.jpg" width="100" height="100" bottom="10" right="10"/>
</mx:Canvas>

The Image is now set to the bottom-right corner with a padding of 10px on it's sides.
That's simple.. innne...

Example of extending UIComponent

/* A simple example of creating a component by extending the UIComponent.
Creates an icon and label aligned vertically.
Usage of : Style Metadata tag, overriding createChildren(), styleChanged(), measure(), updateDisplayList()
*/


package com.kiran.flex.samples
{

import flash.events.Event;
import flash.geom.Matrix;

import mx.controls.Image;
import mx.controls.Label;
import mx.core.UIComponent;


/**
* Border color of background for the icon.
*
* @default 0xFF0000
*/
[Style(name="iconBackgroundColor", type="uint", format="Color")]

[Style(name = "iconBackgroundThickness", type = "Number", format = "Length", inherit = "no")]

public class CustomComponent extends UIComponent
{


private var _icon : Image;
private var _label : Label;

private var _labelPaddingTop : Number;
private var _iconBackgroundColor : uint = 0x000000;
private var _iconBackgroundThickness : Number = 1;

private var _layuotInitialized : Boolean;
public function MainNavItem()
{
super();
labelPaddingTop = 5;
_icon = new Image();
_label = new Label();

_label.styleName = "navButtonTextStyle";
styleName = "mainNavIconStyle";

_icon.addEventListener(Event.INIT, onIconLoaded );

}

override protected function createChildren():void
{
super.createChildren();

addChild( _icon );
addChild( _label );

}


public function onIconLoaded( p_evt : Event = null ):void
{

layoutLabel();
invalidateDisplayList();
}


public function set icon(p_value:Object):void
{
if( p_value is Class )
{
_icon.source = new p_value();
onIconLoaded();
}
else if( p_value is String )
{
_icon.source = p_value;

}

invalidateDisplayList();

}

public function get text():String
{
return _label.text;
}

public function set text(p_value:String):void
{
_label.text = p_value;
name = p_value;
invalidateSize();
invalidateDisplayList();
}

public function get labelPaddingTop():Number
{
return _labelPaddingTop;
}

public function set labelPaddingTop(p_value:Number):void
{
_labelPaddingTop = p_value;
}


public function get idx():Number
{
return _idx;
}

public function set idx(p_value:Number):void
{
_idx = p_value;
}


private function layoutLabel() : void
{
if( !_layuotInitialized )
{

_label.x = width/2 - _label.textWidth / 2;
_label.y = _icon.contentHeight + labelPaddingTop + _icon.y;
}

}

override public function styleChanged(styleProp:String):void
{
super.styleChanged(styleProp);
if(getStyle("iconBackgroundColor") )
{
_iconBackgroundColor = getStyle("iconBackgroundColor") as uint;
}

if(getStyle("iconBackgroundThickness") )
{
_iconBackgroundThickness = getStyle("iconBackgroundThickness") as Number;
}

invalidateDisplayList();
}

override protected function measure():void
{
super.measure();

if( percentWidth )
{
measuredWidth = percentWidth;
}

if( percentHeight )
{
measuredHeight = percentHeight;
}

if( width )
{
measuredWidth = width;
}

if( height )
{
measuredHeight = height;
}

if( !measuredWidth )
{
measuredWidth = _icon.contentWidth;
}

if( !measuredHeight )
{
measuredHeight = _icon.contentHeight + labelPaddingTop + _label.getExplicitOrMeasuredHeight();
}


}
override protected function commitProperties():void
{
super.commitProperties();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight );
for(var i:Number = 0; i < numChildren; i++)
{
var uic : UIComponent = getChildAt( i ) as UIComponent;
uic.setActualSize(uic.getExplicitOrMeasuredWidth(), uic.getExplicitOrMeasuredHeight() );
}
//draw background circle for the icon, looks like a border
_icon.graphics.clear();
_icon.graphics.beginFill( _iconBackgroundColor );
_icon.graphics.drawCircle( _icon.contentWidth / 2, _icon.contentHeight /2, _icon.contentWidth/2 + _iconBackgroundThickness);
_icon.graphics.endFill();


layoutLabel();
_layuotInitialized = true;


}

}
}

Saturday, May 2, 2009

Extend UIComponent

Will give a brief about how to create a custom component extending UIComponent..

UIComponent
1)base class for all components in Flex
2)Can wrap Sprite and Movieclip
3) When extending a UIComponent ensure to override updateDisplayList() and set the width and
height of each component, else nothing is displayed on stage.

In my next post, will give a detailed example about creating a UIComponent.