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.