...
The following diagram shows how the ActionScript List component is composed.
Gliffy Diagram | ||||
---|---|---|---|---|
|
...
The List contains:
- A selection model which encapsulates a data provider as well knowledge about which item is selected (the item and the index).
- A view bean bead that displays the container housing the list control.
- A factory bean bead that can produce instances of item renderers as needed.
- An item renderer to display each element of the list; created by the factory beanbead.
- A mouse controller for the list as a whole that handles the selection for the list.
- A mouse controller for item renderer to handle the hover and selected states.
These parts are specified by the List's style definition in the defaults.css
file:
Code Block |
---|
List { IBeadModel: ClassReference("org.apache.flex.html.staticControls.beads.models.ArraySelectionModel"); IBeadView: ClassReference("org.apache.flex.html.staticControls.beads.ListView"); IBeadController: ClassReference("org.apache.flex.html.staticControls.beads.controllers.ListSingleSelectionMouseController"); IBeadLayout: ClassReference("org.apache.flex.html.staticControls.beads.layouts.NonVirtualVerticalScrollingLayoutVerticalScrollingLayout"); IDataGroup: ClassReference("org.apache.flex.html.supportClasses.DataGroup"); IDataProviderItemRendererMapper: ClassReference("org.apache.flex.html.staticControls.beads.TextItemRendererFactoryForArrayDataDataItemRendererFactoryForArrayData"); IItemRendererClassFactory: ClassReference("org.apache.flex.core.ItemRendererClassFactory"); IItemRenderer: ClassReference("org.apache.flex.html.staticControls.supportClasses.StringItemRenderer"); } |
...
Once the List component and all of the item renderers have been created, the interactive part comes into play. Each item renderer is also a component (strand and beads) and follows the same pattern as other FlexJS components. For the List, the IItemRenderer
is specified in defaults.css
as the StringItemRenderer class which has its own style definition, show here:
Code Block |
---|
StringItemRenderer { IBeadController: ClassReference("org.apache.flex.html.staticControls.beads.controllers.ItemRendererMouseController"); height: 16; } |
...
In the JavaScript, the bead classes are normal classes and events do not propagate from inner to outer class (the UI in JavaScript are HTML elements that the JavaScript classes are wrapping). This event flow makes it a little challenging to have an event dispatched on an item renderer be detected by the ListSingleSelectionMouseController which is listening for events coming through the view bead. In JavaScript, the event dispatched on the item renderer does not have an event chain parent so that detection is not possible.
Code Block |
---|
// from ItemRendererMouseController's handleMouseUp function
this.strand_.get_itemRendererParent().dispatchEvent(newEvent);
|
Instead, the JavaScript implementation of the List component provides a property, itemRendererParent
, to help with the event flow. By dispatching on this property, the same event can reach the ListSingleSelectionMouseController so that the model can be updated in the same way it does in the ActionScript version.
Custom ItemRenderer
This section shows you how to make a custom itemRenderer for a List control.
The first thing to do is map out your data: what does your data look like? For this example, the data is a list of products each having an image of the product, the product's ID, title, and stock status (in stock or out of stock).
The next thing to do is design the itemRender: what do you need to show in each itemRenderer? For this example, the itemRenderer will show an image, a title, and single detail line of text which will be the stock status.
Example ItemRenderer
FlexJS comes with two pre-built itemRenderers and there will probably be more. The TextItemRenderer is used exclusively for arrays of strings and is used with the SimpleList control. The DataItemRenderer is useful for more complex data and that is the class your itemRenderers will mostly extend.
Code Block |
---|
public class ProductItemRenderer extends DataItemRenderer
{
public function ProductItemRenderer()
{
super();
}
private var image:Image;
private var title:Label;
private var detail:Label;
}
|
The example, started above, shows the ProductItemRenderer extending DataItemRenderer and defining the components it needs to display the image, title, and detail elements.
Every itemRenderer should override the addedToParent()
function and instantiate the components that make up the itemRenderer, as shown here:
Code Block |
---|
override public function addedToParent():void
{
super.addedToParent();
image = new Image();
addElement(image);
title = new Label();
addElement(title);
detail = new Label();
addElement(detail);
}
|
The addedToParent()
function is called when the itemRenderer has been instantiated and added to the display list. In the example, the image, title, and detail are created and added to the itemRenderer's display list.
Once an itemRenderer has been created, it will be given the data to show by setting the itemRenderer's data
property (virtualized lists, which are not yet developed in FlexJS, will recycle the itemRenderers and set the data
property whenever they can used).
Code Block |
---|
override public function set data(value:Object):void
{
super.data = value;
image.source = data.image;
title.text = data.title;
detail.text = data.detail;
}
|
The override of the data
property setter should always call super.data = value
as the first thing to ensure the data
getter returns the correct thing. All this override does it set the appropriate property on the components in the itemRenderer. For example, the title
component has its text
property set from the data' title
property.
Finally, the itemRenderer should respond to changes in its size. The base class, DataItemRenderer, intercepts the events that trigger this and automatically calls its adjustSize()
function.
Code Block |
---|
override public function adjustSize():void
{
var cy:Number = this.height/2;
image.x = 4;
image.y = cy - 16;
image.width = 32;
image.height = 32;
title.x = 40;
title.y = cy - 16;
detail.x = 40;
detail.y = cy;
updateRenderer();
}
|
This itemRenderer sample determines the vertical center of its space and then positions the image, title, and detail components from that. Your itemRenderer can do whatever it needs to size and position its component children.
The final step adjustSize()
should do is call updateRenderer()
which will take care of any background color change in case the itemRenderer is selected or being hovered by the mouse.
While this example does not do it, your itemRenderer may also want to override updateRenderer()
and provide different experiences for the hovered and selected states.
Making it Work
Once you have created your itemRenderer you can add it a List control via a style definition (this one appears in the style block of MyInitialView):
Code Block |
---|
<fx:Style>
@namespace basic "library://ns.apache.org/flexjs/basic";
@namespace sample "products.*";
sample|ProductItemRenderer {
height: 40;
IBeadController: ClassReference("org.apache.flex.html.beads.controllers.ItemRendererMouseController");
}
</fx:Style>
<basic:List id="productList" x="20" y="400" width="200" height="150" className="productList">
<basic:beads>
<basic:ConstantBinding
sourceID="applicationModel"
sourcePropertyName="products"
destinationPropertyName="dataProvider" />
</basic:beads>
</basic:List>
|
The itemRenderer, ProductItemRenderer, is actually in the products
package so a @namespace
is used to identify it in the style definition. The style definition sets the ProductItemRenderer's height to 40 pixels and makes it use the ItemRendererMouseController to handle the mouse events for roll over, roll out, up, and down which translate to the itemRenderer's hover and selected states.
JavaScript
Note |
---|
The example is for ActionScript only. Custom JavaScript itemRenderers are a work in progress. |