2012/06/17

Time to Eeplace TWaver’s Tree with FastTree

In TWaver Flex edition 2.1 the latest FastTree has been released, the literal meaning of which is Tree “at a higher speed.” It is similar to Tree in function, not multiplexing Flex’s Tree. FastTree is superior to the ordinary Tree in performance and speed, for it only refreshes the visible part. Its imperfection lies in the lack of some decoratory functions, such as the animation effects when expanding nodes and the highlight in the current line when moving the mouse. In the following codes we will display with an example in which aspects FastTree is fast.
  1. Faster in sequencing and filtering
  2. The following example displays the filtering and sequencing functions of FastTree and Tree respectively: At the moment when 10 thousand nodes have been loaded in FastTree, after inputting a filter condition, the result will show up right away; with 1 thousand nodes loaded in the ordinary Tree, 2-3 seconds bring the result out.

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                    xmlns:twaver="http://www.servasoftware.com/2009/twaver/flex"
                    layout="absolute" creationComplete="init()">

        <mx:Script>
            <![CDATA[
                import twaver.*;

                private var input:String = "";

                private function init():void {
                    // Init Filter.
                    fastTree.visibleFunction = function(node:IData):Boolean{
                        if(input == ""){
                            return true;
                        }
                        return isVisible(node);
                    };

                    // Init Sort.
                    fastTree.compareFunction = function(d1:IData, d2:IData):int {
                        if(d1.name > d2.name){
                            return 1;
                        }else if(d1.name == d2.name){
                            return 0;
                        }else{
                            return -1;
                        }
                    };

                    // Init DataBox
                    var i:int = 100;
                    var box:DataBox = fastTree.dataBox;
                    while(i--){
                        var node:Node = new Node();
                        node.name = 'n-' + i;
                        box.add(node);
                        var ii:int = 100;
                        while(ii--){
                            var child:Node = new Node();
                            child.name =  'n-' + i + '-' +ii;
                            node.addChild(child);
                            box.add(child);
                        }
                    }

                    // Expand All Nodes
                    fastTree.callLater(function():void {
                        fastTree.expandAll();
                    });
                }

                // All parents are visible if child is visible
                private function isVisible(item:IData):Boolean {
                    if(item.name.toLowerCase().indexOf(this.input) >= 0) {
                        return true;
                    }
                    var result:Boolean = false;
                    item.children.forEach(function(child:IData):Boolean {
                        if(isVisible(child)) {
                            result = true;
                            return false;
                        }
                        return true;
                    });
                    return result;
                }

                // Refresh FastTree when input is changed.
                private function handleSearchTextChanged():void {
                    this.input = this.textSearch.text.toLowerCase();
                    fastTree.invalidateModel();
                }
            ]]>
        </mx:Script>

        <mx:VBox width="100%" height="100%" >
            <mx:TextInput id="textSearch" change="handleSearchTextChanged()" />
            <mx:HBox width="100%" height="100%" >
                <twaver:FastTree id="fastTree" width="100%" height="100%"/>
            </mx:HBox>
        </mx:VBox>
    </mx:Application>

    TestTreeSortAndFilter.mxml:

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                    xmlns:twaver="http://www.servasoftware.com/2009/twaver/flex"
                    layout="absolute" creationComplete="init()">

        <mx:Script>
            <![CDATA[
                import twaver.*;

                private var input:String = "";

                private function init():void {
                    // Init Filter.
                    tree.visibleFunction = function(node:IData):Boolean{
                        if(input == ""){
                            return true;
                        }
                        return isVisible(node);
                    };

                    // Init Sort.
                    tree.compareFunction = function(d1:IData, d2:IData):int {
                        if(d1.name > d2.name){
                            return 1;
                        }else if(d1.name == d2.name){
                            return 0;
                        }else{
                            return -1;
                        }
                    };

                    // Init DataBox
                    var i:int = 100;
                    var box:DataBox = tree.dataBox;
                    while(i--){
                        var node:Node = new Node();
                        node.name = 'n-' + i;
                        box.add(node);
                        var ii:int = 10;
                        while(ii--){
                            var child:Node = new Node();
                            child.name =  'n-' + i + '-' +ii;
                            node.addChild(child);
                            box.add(child);
                        }
                    }

                    // Expand All Nodes
                    tree.callLater(function():void {
                        tree.expandAll();
                    });
                }

                // All parents are visible if child is visible
                private function isVisible(item:IData):Boolean {
                    if(item.name.toLowerCase().indexOf(this.input) >= 0) {
                        return true;
                    }
                    var result:Boolean = false;
                    item.children.forEach(function(child:IData):Boolean {
                        if(isVisible(child)) {
                            result = true;
                            return false;
                        }
                        return true;
                    });
                    return result;
                }

                // Refresh tree when input is changed.
                private function handleSearchTextChanged():void {
                    this.input = this.textSearch.text.toLowerCase();
                    tree.updateCompareAndVisibility();
                }
            ]]>
        </mx:Script>

        <mx:VBox width="100%" height="100%" >
            <mx:TextInput id="textSearch" change="handleSearchTextChanged()" />
            <mx:HBox width="100%" height="100%" >
                <twaver:Tree id="tree" width="100%" height="100%"/>
            </mx:HBox>
        </mx:VBox>
    </mx:Application>
     
  3. Faster in lazy load
  4. In the following example we have imitated the lazy load. 500 child nodes will be loaded every time you unfold a tree node: it costs only tens of milliseconds to load even when tens of thousands of nodes have already been unfolded under FastTree; it needs to takes over 10 seconds to load nodes when 3 thousand nodes have been expanded under Tree.

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init();"
                    xmlns:tw="http://www.servasoftware.com/2009/twaver/flex">
        <mx:Script>
            <![CDATA[
                import twaver.*;
                import twaver.controls.*;

                private var box:DataBox = new DataBox();

                private function init():void {
                    // Init DataBox
                    for(var i:int=0; i<40; i++){
                        var data:Data = new Data();
                        data.name = "Data" + i;
                        box.add(data);
                    }
                    fastTree.dataBox = box;

                    // Setup Lazy Load
                    fastTree.branchFunction = function(data:IData):Boolean {
                        return true;
                    };

                    // Load Children when expanding
                    fastTree.addInteractionListener(function(event:FastTreeInteractionEvent):void{
                        if(event.kind == FastTreeInteractionEvent.EXPAND &&
                            event.data.childrenCount == 0){
                            loadDatas(event.data);
                        }
                    });
                }

                // Load 500 Nodes
                private function loadDatas(data:IData):void{
                    var startTime:Date = new Date();
                    for(var j:int=0; j<500; j++){
                        var child:Data = new Data();
                        child.name = data.name + "," + j;
                        child.parent = data;
                        box.add(child);
                    }
                    console.text = "Count:"+box.count+"\tExpand Time:"+(new Date().getTime()-startTime.getTime())+"ms";
                }
            ]]>
        </mx:Script>

        <mx:Label id="console" width="400"/>
        <tw:FastTree id="fastTree" width="400" height="100%" editable="true" backgroundColor="#FFFFFF"/>
    </mx:Application>


    TestTreeLazyLoad.mxml:

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init();"
                    xmlns:tw="http://www.servasoftware.com/2009/twaver/flex">
        <mx:Script>
            <![CDATA[
                import twaver.*;
                import twaver.controls.*;

                private var box:DataBox = new DataBox();

                private function init():void {
                    // Init DataBox
                    for(var i:int=0; i<40; i++){
                        var data:Data = new Data();
                        data.name = "Data" + i;
                        box.add(data);
                    }
                    tree.dataBox = box;

                    // Setup Lazy Load
                    tree.branchFunction = function(treeData:TreeData):Boolean {
                        return true;
                    };

                    // Load Children when expanding
                    tree.addInteractionListener(function(event:TreeInteractionEvent):void{
                        if(event.kind == TreeInteractionEvent.EXPAND_TREE_DATA &&
                                event.treeData.data != null &&
                                event.treeData.data.childrenCount == 0){
                            loadDatas(event.treeData.data);
                        }
                    });
                }

                // Load 500 Nodes
                private function loadDatas(data:IData):void{
                    var startTime:Date = new Date();
                    for(var j:int=0; j<500; j++){
                        var child:Data = new Data();
                        child.name = data.name + "," + j;
                        child.parent = data;
                        box.add(child);
                    }
                    console.text = "Count:"+box.count+"\tExpand Time:"+(new Date().getTime()-startTime.getTime())+"ms";
                }
            ]]>
        </mx:Script>

        <mx:Label id="console" width="400"/>
        <tw:Tree id="tree" width="400" height="100%" editable="true" backgroundColor="#FFFFFF"/>
    </mx:Application>
     
  5. Faster operational speed of ExpandAll with huge amount of data
  6. When 50 thousand nodes have been loaded by FastTree, clicking all to unfold them will only cost you several milliseconds; by ordinary Tree it turns out to be abnormal after 1 minute. TestFastTreeExpandAll.mxml: 
    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                    xmlns:tw="http://www.servasoftware.com/2009/twaver/flex"
                    paddingLeft="0" paddingRight="0" paddingTop="0" paddingBottom="0" backgroundColor="0xFFFFFF"
                    creationComplete="init()">
        <mx:Script>
            <![CDATA[
                import twaver.*;

                private var box:DataBox = new DataBox();

                private function init():void {
                    this.initBox();
                    trace(box.count);
                    fastTree.dataBox = box;
                }

                private function initBox():void {
                    var root:Data = this.addData("Root");
                    for (var k:int = 0; k < 10; k++) {
                        var ip:String = "10.0." + k + ".";
                        var count:int = 0;
                        var data:Data = this.addData(ip + count++);
                        data.parent = root;

                        for (var i:int = 0; i < 10; i++) {
                            var iData:Data = this.addData(ip + count++);
                            iData.parent = data;
                            for (var j:int = 0; j < 100; j++) {
                                var jData:Data = this.addData(ip + count++);
                                jData.parent = iData;
                            }
                        }
                    }
                }

                private function addData(name:String):Data {
                    var data:Data = new Data();
                    data.name = name;
                    data.toolTip = name;
                    this.box.add(data);
                    return data;
                }
            ]]>
        </mx:Script>

        <mx:HDividedBox width="100%" height="100%">
            <tw:FastTree id="fastTree" width="200" height="100%"/>
            <mx:HDividedBox width="100%" height="100%">
                <mx:Canvas id="canvas" width="100%" height="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off">
                    <mx:HBox id="toolbar" top="0" left="0" horizontalGap="0">
                        <mx:Button label="Expand All" click="fastTree.expandAll();"/>
                        <mx:Button label="Collapse All" click="fastTree.collapseAll();"/>
                    </mx:HBox>
                </mx:Canvas>
            </mx:HDividedBox>
        </mx:HDividedBox>
    </mx:Application>
     
    TestTreeExpandAll.mxml:
    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                    xmlns:tw="http://www.servasoftware.com/2009/twaver/flex"
                    paddingLeft="0" paddingRight="0" paddingTop="0" paddingBottom="0" backgroundColor="0xFFFFFF"
                    creationComplete="init()">
        <mx:Script>
            <![CDATA[
                import twaver.*;

                private var box:DataBox = new DataBox();

                private function init():void {
                    this.initBox();
                    trace(box.count);
                    tree.dataBox = box;
                }

                private function initBox():void {
                    var root:Data = this.addData("Root");
                    for (var k:int = 0; k < 10; k++) {
                        var ip:String = "10.0." + k + ".";
                        var count:int = 0;
                        var data:Data = this.addData(ip + count++);
                        data.parent = root;

                        for (var i:int = 0; i < 10; i++) {
                            var iData:Data = this.addData(ip + count++);
                            iData.parent = data;
                            for (var j:int = 0; j < 100; j++) {
                                var jData:Data = this.addData(ip + count++);
                                jData.parent = iData;
                            }
                        }
                    }
                }

                private function addData(name:String):Data {
                    var data:Data = new Data();
                    data.name = name;
                    data.toolTip = name;
                    this.box.add(data);
                    return data;
                }
            ]]>
        </mx:Script>

        <mx:HDividedBox width="100%" height="100%">
            <tw:Tree id="tree" width="200" height="100%"/>
            <mx:HDividedBox width="100%" height="100%">
                <mx:Canvas id="canvas" width="100%" height="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off">
                    <mx:HBox id="toolbar" top="0" left="0" horizontalGap="0">
                        <mx:Button label="Expand All" click="tree.expandAll();"/>
                        <mx:Button label="Collapse All" click="tree.collapseAll();"/>
                    </mx:HBox>
                </mx:Canvas>
            </mx:HDividedBox>
        </mx:HDividedBox>
    </mx:Application>
     
Besides, there are no such bugs as listed below:
Finally, the use of FastTree is similar to that of ordinary Tree but attention need to be paid to some differences between them. Most of them are that all TreeData's have been replaced by IData's. Through the example above you can also find some differences.
  1. The methods to make it refresh after the change of Filter or Sequencing Function.
  2. Replacing
    public function updateCompareAndVisibility(treeData:TreeData = null):void
    public function invalidateTreeData(treeData:TreeData = null):void
    with
    public function invalidateModel():void
     
  3. The methods to judge whether nodes are expanded.
  4. Replacing
    public function isOpen(data:IData):Boolean
    public function isDataOpen(data:IData):Boolean
    with
    public function isExpanded(data:IData):Boolean 
     
  5. The methods to judge whether nodes are visible
  6. Replacing
    public function isTreeDataVisible(treeData:TreeData):Boolean
    public function isDataVisible(data:IData):Boolean
    with
    public function isVisible(data:IData):Boolean
     
  7. The method to judge whether nodes can be expanded
  8. Replacing
    public function isBranch(treeData:TreeData):Boolean
    with
    public function isBranch(data:IData):Boolean
     
  9. The methods to spot a particular node
  10. Replacing
    public function getTreeData(data:IData):TreeData
    public function getDataIndex(data:IData):int
    public function getTreeDataByIndex(index:int):TreeData
    public function getTreeDataByMouseEvent(event:MouseEvent):TreeData
    public function getTreeDataByContextMenuEvent(event:ContextMenuEvent):TreeData
    with
    public function getRowIndexByData(data:IData):int
    public function getRowIndexById(id:Object):int
    public function getDataByRowIndex(index:int):IData
    public function getRowIndexByMouseEvent(e:MouseEvent):int
    public function getDataByMouseEvent(e:MouseEvent):IData
    public function getDataByContextMenuEvent(event:ContextMenuEvent):IData
    public function getRowIndexByStagePoint(point:Point):int
    public function getDataByStagePoint(point:Point):int
     
  11. The method to get the root nodes of the tree
  12. Replacing
    public function get rootTreeData():TreeData
    with
    public function get rootData():IData
     
  13. The method to expand and collapse the tree node
  14. Replacing
    public function expandData(data:IData, animate:Boolean = false):void
    public function collapse(data:IData, animate:Boolean = false):void
    with
    public function expand(data:IData):void
    public function collapse(data:IData):void
     
  15. The method to perform an operation for each tree node
  16. Replacing
    public function forEachTreeData(callbackFunction:Function, treeData:TreeData = null):void
    with
    tree.rowDatas.forEach
     
  17. Substituting TreeInteractionEvent with FastTreeInteractionEvent
  18. public function addInteractionListener(listener:Function, priority:int = 0, useWeakReference:Boolean = false):void 
     
  19. The method to get the user-defined components of tree nodes
  20. Replacing
    public function getIconsComponents(treeData:TreeData):Array
    with
    public function getIconsComponents(data:IData):Array
     
  21. The parameter TreeData of the methods iconsComponentsFunction and branchFunction has been replaced by IData.
  22. public function get iconsComponentsFunction():Function
    public function get branchFunction():Function

2012/06/04

The Deployment of and Solutions to TWaver Flex Demo on Flash Builder4

When users deploy TWaver Flex Demo on FB and then copy demo of src into it, they will find that some error messages will pop out and stop its function.
The main reason is that the latest Flash Builder applies SDK4.0 or 4.1 as default while many users are still using the edition SDK3. So in order to be compatible with editions SDK 3.4 and above, some of our Demo's codes are based on SDK3.
This does never mean that our SDK does not support SDK4: it is out of question that twaver.swc is able to run with SDK4, with just some small adjustments are to be made to our Demo. There are three methods to settle it down:
  1. Directly running with edition 3.5 as it is shown in the following picture: 


  2. If you would like to edit with SDK4, please choose "MX only" from the two options under Build Path of the project:
  3.  

  4. If you choose "MX+Spark", you need to set the following parameters.
  5. (1)When initializing init, you need to set the following two twaver's defaults——the background parameters of network, otherwise the rectangular-marquee selection will not be available when the background is null. 
    Defaults.NETWORK_BACKGROUND_ALPHA = 0;
    Defaults.NETWORK_BACKGROUND_COLOR = 0xFF0000;
    (2)Some parameters in TWaver products' demos can just be deleted, for Flex4 has changed the property name of style in just several components. You could refer to the following link for more details:      http://opensource.adobe.com/wiki/display/flexsdk/More+Styles+for+Spark+Skins

         borderVisibility has replaced borderThickness and
         Can turn border on and off only
         dropShadowVisible has replaced dropShadowEnabled.
         contentBackgroundAlpha has replaced backgroundAlpha.
         contentBackgroundColor has replaced backgroundColor.
    (3)If you need zoom in and out the texts of label in the CloudLayout at lower left side of TWaver products' demo, then the addition of the parameter embedAsCFF='false' in Flex4 will be needed.  
    http://macmartine.com/blog/2009/10/embedding-fonts-in-flex-4.html
    [Embed(source='Helvetica.ttf',
    fontName="demoFont",
    advancedAntiAliasing="false",
    mimeType="application/x-font",
    embedAsCFF='false')]
You can choose model MX+only in SDK3.5 or SDK4 when running the demo to avoid troubles. As to which SDK to choose, you could do anyone according to your detailed requirements.

In general, twaver SDK twaver.swc completely supports the development of Flex3 and Flex4. You just need pay attention to the above three points when applying the blended model MX+Spark.

2012/05/30

The Ways to Use Renderer and Editor in TWaverJava

How to use Renderer and Editor in TWaverJava?
To reduce the workload of users, TWaver has predefined Renderer and Editor as default functions.
Users can use them directly, for example:
twaver.table.renderer.StringRenderer
twaver.table.renderer.StrokeRenderer
twaver.table.renderer.FontRenderer
twaver.table.renderer.EnumTypeRenderer
TWaver's Table and Sheet has offered flexible deployment, enabling users to use customized renderer and editor, such as ValidateValueDemo.

Now taking ValidateValueDemo as an example, we will introduce the use of Renderer and Editor as follows: (Notice that in this example there are a few tables and renderers applying xml to deploy. The effects are the same if using API.)
  1. Let's first see how to set renderer and editor.
  2. They are mainly in correspondence with the two ways setRendererClass and setEditorClass of ElementAttribute, such as the following XML extract: 
    <attribute clientPropertyKey="MyProp3"
                displayName="1980-03-06~2080-04-23"
                icon="/demo/sheet/validate/markIcon.png"
                renderer="twaver.table.renderer.DateRenderer@yyyy-MM-dd"
                editor="twaver.table.editor.SpinnerDateEditor@1980-03-06|2080-04-23|yyyy-MM-dd">
        <param key="nullable" value="false"/>
        <param key="message" value="Date field can not be null."/>
    </attribute>
    Of course, users can also define a render, such as:
    <attribute
        clientPropertyKey="*"
        displayName="ID"
        renderer="demo.table.legend.RowIDRenderer"/>
    See demo.table.legend.RowIDRenderer for the code of RowIDRenderer
  3. How to pass by parameters to self-defined renderer and editor?
  4. Sometimes we need to pass by some parameters to renderer, such as DateRenderer. Maybe we need to customize the display format. When a Renderer object is created in TWaver, strings in Renderer will be analyzed. 
Here is the extract:
String[] parameterVlaues = value.split("\\|");
Class[] parameterTypes = new Class[parameterVlaues.length];
for(int i=0; i<parameterVlaues.length; i++){
        parameterTypes[i] = String.class;
    }
Class clazz = ReflectUtils.forName(className);
return clazz.getConstructor(parameterTypes).newInstance(parameterVlaues);
In this way we can pass by parameters to self-defined renderer and editor through  For example,After parameters are passed by to renderer="twaver.table.renderer.DateRenderer@yyyy-MM-dd  of DataRender," the format used in DateRenderer is "yyyy-MM-dd."
Another function of renderer and editor used more frequently is twaver.table.renderer.EnumTypeRenderer and twaver.table.editor.EnumTypeEditor.
The Constructor of class EnumTypeEditor is given as follows:
public EnumTypeEditor(String enumTypeName, String nullable, String alignment) {
If the parameter required to be passed by is the registered enumTypeName of EnumTypeManager, for example:
EnumTypeManager.getInstance().registerEnumTypes("City", new EnumType[] {
               new EnumType("N", "New York"),
               new EnumType("T", "Tokyo"),
               new EnumType("L", "London")});
Then values can be passed by through the following way:
renderer="twaver.table.renderer.EnumTypeRenderer@City"
editor="twaver.table.editor.EnumTypeEditor@City">
 "nullable" means that whether null is allowed to show in the drop-down list. If you would not like null to be appeared in the drop-down list, you could set the value as false:
renderer=”twaver.table.renderer.EnumTypeRenderer@City|false”
If your project has applied Renderer and Editor, we recommend you to read ValidateValueDemo attentively.

SWF Loading and Data Interaction on Flex

During the process of modularity development, some users may make each functional module into a sub swf respectively. Then a main program will load and display it and perform data interaction respectively.
In this process two main steps are included:
  1. Loading swf
  2. This step is quite easy, for you can just use the method “load” in SWFLoader to load swf. 
    loader.load("SWFLoadedDemo.swf"); 
  3. The data interaction between the main program and the sub swf
  4. This can be realized by the following code:
    var obj:Object=SystemManager(loader.content).application; tree.dataBox=obj.getBox();
There is one thing we need to pay attention to: Generally, we will add the following event to perform data loading.
loader.addEventListener(Event.COMPLETE, function(ev:Event):void
                   {
                        loadSWF(ex);
                    });
It is useless to fetch data after loading swf without the creation of swf. So if you just write the above codes, the null pointer will turn out as being abnormal.
The solution is provided as follows: Performing data interaction after the knowledge of the creation of swf by means of listening FlexEvent.APPLICATION_COMPLETE.
loader.addEventListener(Event.COMPLETE, function(ev:Event):void
           {
               var loadedSM:SystemManager=SystemManager(loader.content);
               loadedSM.addEventListener(FlexEvent.APPLICATION_COMPLETE, function(ex:Event):void
                  {
                      loadSWF(ex);
                   });
             });
Here is the detailed code:
SWFDemo.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                creationComplete="init();"
                layout="absolute"
                width="100%"
                height="100%"
                xmlns:tw="http://www.servasoftware.com/2009/twaver/flex">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.controls.SWFLoader;
            import mx.events.FlexEvent;
            import mx.managers.SystemManager;
            private var loader:SWFLoader=new SWFLoader();

            private function init():void
            {
                loader.percentWidth=100;
                loader.percentHeight=100;
                swfPanel.addChild(loader);
                loader.addEventListener(Event.COMPLETE, function(ev:Event):void
                    {
                        var loadedSM:SystemManager=SystemManager(loader.content);
                        loadedSM.addEventListener(FlexEvent.APPLICATION_COMPLETE, function(ex:Event):void
                            {
                                loadSWF(ex);
                            });
                    });

                loader.load("SWFLoadedDemo.swf");
            }

            private function loadSWF(e:Event):void
            {
                var obj:Object=SystemManager(loader.content).application;
                tree.dataBox=obj.getBox();
            }
        ]]>
    </mx:Script>
    <mx:HBox width="100%"
             height="100%">
        <mx:VBox width="15%"
                 height="100%">
            <mx:Button label="LOAD"
                       click="loadSWF(event)"/>
            <tw:Tree id="tree"
                     width="100%"
                     height="100%"/>
        </mx:VBox>
        <mx:VBox width="85%"
                 height="100%"
                 id="swfPanel">
            <!--<mx:SWFLoader height="100%"
                 width="100%"
                 id="loader"
                 source="SWFLoadedDemo.swf"
                 showBusyCursor="true">
                 </mx:SWFLoader>-->
        </mx:VBox>
    </mx:HBox>
</mx:Application>
SWFLoadedDemo.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
                layout="absolute"
                creationComplete="init();"
                width="100%"
                height="100%"
                xmlns:tw="http://www.servasoftware.com/2009/twaver/flex">
    <mx:Script>
        <![CDATA[
            import twaver.ElementBox;
            import twaver.Node;
            private var box:ElementBox=new ElementBox();

            private function init():void
            {
                network.elementBox=box;

                for (var i:int=0; i < 5; i++)
                {
                    var n:Node=new Node();
                    n.name="node" + i;
                    n.setLocation(Math.random() * 800, Math.random() * 600);
                    box.add(n);
                }
            }

            public function getBox():ElementBox
            {
                return box;
            }
        ]]>
    </mx:Script>
    <tw:Network id="network"
                width="100%"
                height="100%"/>
</mx:Application>

2012/05/21

Operations on Local Interactions of Flex

Flash is unable to interact with local programs before the release of Flash Player 10, for generally the data has to be sent to the background and then sent from the background to the foreground to be saved to perform some operations like image saving. There is no doubt that this method is of much trouble, involving writing a series of  F/B interactive processing. Fortunately, in Flash Player 10, Adobe has offered some interfaces to make the local interactions possible, such as local file saving, file opening and file uploading. Taking TWaver's FlexDemo as an example, on the toolbar, operations like saving images & xml and opening xml have been provided. You could see the following instance:


You could interact with local files conveniently by clicking these several buttons. Image saving and XML saving are fairly easy, just calling the "save"method of FileReference. XML opening calls the "browse"method of FileReference. After loading the data, you could read it in directly by var xmlText:String = fr.data.readUTFBytes(fr.data.length);.  The code is as follows:

var fr:FileReference = new FileReference();
if (fr.hasOwnProperty("browse")) {
  fr.addEventListener(Event.SELECT, function(e:Event):void {
    fr.load();
  });
  fr.addEventListener(Event.COMPLETE, function(ex:Event):void {
    var xmlText:String = fr.data.readUTFBytes(fr.data.length);
    var serializer:XMLSerializer = new XMLSerializer(network.elementBox);
    serializer.deserialize(xmlText,network.currentSubNetwork);
  });
  var fileFilter:FileFilter = new FileFilter("XML: (*.xml)", "*.xml");
  fr.browse([fileFilter]);
  } else {
    Alert.show("install Flash Player 10 to run this feature", "Not Supported");
  }

However, when many clients are trying to run Demo in FB and to save images, there will be an error message box reading,"Install Flash Player 10 to run this feature." The reason is that not only the client-side is required to  have Flash Player10, but the environment argument (while compiling) to be -target player=10.0.0.Besides,  there will also be an alert if you just call fr.load();in Flash Builder4 with SDK4.1. But this problem can be settled down by adding -target-player=10.0.0.

For more detailed information, please refer to exporting images in Flex.

To Export Images in Flex

There are several procedures to realize image exporting in Flex.
1.Make a UI component and draw the content to be exported on it.
2.Making a BitmapData object and draw the UI component into it.
3.Convert the contents of BitmapData into binary data of ByteArray through PNGEncoder or JPEGEncoder.

You must have the image data in RAM after the above procedures. You are quite lucky if you have TWaver's Network component, for by the means of the function exportAsBitmapData(logicalRect:Rectangle = null, zoom:Number = 1):BitmapData of network you are able to convert any area of a image of any percentage into a BitmapData object.

But here it is the Last Mile that really matters. The image information in the RAM is invisible and untouchable, so generally we need to export it to local files. The traditional method we took before is to transmit the binary data of ByteArray to the background by means of HTTP, and then redirected it as an issued file to be saved by users. Undoubtedly, this method was of much demerits: data at the client side had to take the trouble to transmit to the server-side and then returned to the same side. In an age of green and conservation, this method seems like "crime." Fortunately, Adobe succeeded in adding a green and conservative function FileReference.save(), thus enabling us to save the data of the RAM of the client-side in the local hard disk.

You may have found that only Flex4 has the function FileReference.save() . In fact, there are many projects which are developed on the basis of SDK of Flex3 now, including TWaver Flex Demo. How can we use this function in products based on Flex3? You could find the answer in TWaver's Flex Demo. Just two steps:

Step 1: Introducing in the FileReference object which is of the class Object in the code. By using Object of dynamic, you could call a function or compile the passing conditions. But you need to write the function hasOwnProperty to judge whether direct image-saving can be realized.

public static function addExportButton(toolbar:Box,network:Network):Button{
    return addButton(toolbar, "Export Image", DemoImages.export, function():void{
        var fr:Object = new FileReference();
        if(fr.hasOwnProperty("save")){
            var bitmapData:BitmapData = network.exportAsBitmapData();
            var encoder:PNGEncoder = new PNGEncoder();
            var data:ByteArray = encoder.encode(bitmapData);
            fr.save(data, 'network.png');
        }else{
            Alert.show("install Flash Player 10 to run this feature", "Not Supported");
        }
    });
}

Step2: Add “-target-player=10.0.0”into the additional compiler-environment argument as in the following picture:



2012/05/17

Making a Dashboard Within 5 Minutes

Almost every program has such requirement: Lots of charts being put together to present varieties of statistics of data. In English it is called Dashboard, while there hasn't been an appropriate equivalent in Chinese. Someone just call it dash “dash board”, which is somewhat weird.

It is not a problem to make a Dashboard as soon as you have TWaver's Charts. You could make it by just putting the many Charts into a JPanel and then perfecting it by making a Border around it  with some buttons like min button and max button to operate it.

Let's first look at its effect:


Here is the code to it. We first make a Chart as an example. We recommend you to use other Chart of various kinds to enrich the visual effects.

public class MyBarChart extends BarChart {

   private Element data1 = new Node();
   private Element data2 = new Node();

     public MyBarChart() {
  this.setBarType(TWaverConst.BAR_TYPE_GROUP);
  this.setShadowOffset(10);
  this.setYScaleTextVisible(true);
  this.setYScaleMinTextVisible(true);
  this.setUpperLimit(60);
  this.setYScaleValueGap(10);


  this.addXScaleText("Jan");
  this.addXScaleText("Feb");
  this.addXScaleText("Mar");


  this.addElement(data1, "USD", TWaverUtil.getRandomColor());
  this.addElement(data2, "RMB", TWaverUtil.getRandomColor());
      
  this.addValue(data1,getRandomData(),getRandomData(),getRandomData());
  this.addValue(data2,getRandomData(),getRandomData(),getRandomData()); }  
private int getRandomData() {
  return TWaverUtil.getRandomInt(60);
}
private void addElement(Element element, String name, Color color) {
  element.setName(name);
  element.putChartColor(color);
  box.addElement(element);
}
private void addValue(Element element, double value1, double value2, double value3) {
  element.addChartValue(value1);
  element.addChartValue(value2);
  element.addChartValue(value3);
}
}


Next, let's turn to the main program: Just put a Panel of GridLayout and a Border outside it into the window.



public class Test extends JComponent {
   public Test() {
      this.setBorder(BorderFactory.createLineBorder(Color.lightGray, 1));
      this.setLayout(new BorderLayout());
      this.add(createHeaderPane(), BorderLayout.NORTH);
      this.add(createChart(), BorderLayout.CENTER);
}

   private JComponent createChart() {
      return new MyBarChart() ;
}

   private JPanel createHeaderPane() {
      JPanel headerPane = new JPanel(new BorderLayout());
      headerPane.setOpaque(false);
      headerPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
      headerPane.add(createNorthPane(), BorderLayout.NORTH);

      JTextArea txtDescription = new JTextArea("Here is the description for this portlet.");
      txtDescription.setEditable(false);
      txtDescription.setEnabled(false);
      txtDescription.setOpaque(false);
      txtDescription.setFont(new Font("Dialog", Font.ITALIC, 12));
      txtDescription.setDisabledTextColor(Color.gray);
      txtDescription.setLineWrap(true);
      txtDescription.setBorder(createUnderlineBorder());
      headerPane.add(txtDescription, BorderLayout.CENTER);

      return headerPane;
   }

   private JPanel createNorthPane() {
       JPanel northPane = new JPanel(new BorderLayout());
       northPane.setOpaque(false);
       JLabel lbTitle = new JLabel("Month Sales");
       lbTitle.setFont(new Font("Dialog", Font.BOLD, 12));
       lbTitle.setForeground(new Color(0, 0, 255, 100));
       northPane.add(lbTitle, BorderLayout.CENTER);
       JPanel buttonPane = createButtonPane();
       northPane.add(buttonPane, BorderLayout.EAST);
       return northPane;
    }

    private JPanel createButtonPane() {
       JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
       buttonPane.setOpaque(false);
       JButton btnDropDown = new JButton();
       btnDropDown.setOpaque(false);
       btnDropDown.setMargin(new Insets(0, 0, 0, 0));
       btnDropDown.setIcon(TWaverUtil.getIcon("/dashboard/dropdown.png"));
       btnDropDown.setBorder(null);
       buttonPane.add(btnDropDown);
       JButton btnClose = new JButton();
       btnClose.setMargin(new Insets(0, 0, 0, 0));
       btnClose.setIcon(TWaverUtil.getIcon("/dashboard/close.png"));
       btnClose.setBorder(null);
       btnClose.setOpaque(false);
       btnClose.addActionListener(new ActionListener() {
           public void actionPerformed(ActionEvent e) {
               Test.this.setVisible(false);
           }
       });
       buttonPane.add(btnClose);
       return buttonPane;
    }

    private static Border createUnderlineBorder() {
       return new Border() {
           public void paintBorder(Component c,
                            Graphics g,
                            int x,
                            int y,
                            int width,
                            int height) {
                    g.setColor(Color.lightGray);
                    g.drawLine(x,
                    y + height - 2,
                    x + width,
                    y + height - 2);
           }

           public Insets getBorderInsets(Component c) {
                    return new Insets(0, 0, 2, 0);
           }

           public boolean isBorderOpaque() {
                    return true;
           }
       };
    }


    public static void main(String[] args) {
       JFrame frame = new JFrame();
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       JPanel pane = new JPanel(new GridLayout(3, 3, 10, 10));
       pane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
       pane.setBackground(Color.white);
       frame.add(pane);
       for (int i = 0; i < 9; i++) {
           pane.add(new Test());
       }
       frame.setSize(1000, 700);
       TWaverUtil.centerWindow(frame);
       frame.setTitle("TWaver Dashboard");
       frame.setVisible(true);
    }
}

Finally, we have here attached all codes and pictures.