Usability


13
May 11

Export to Jpeg in flex.

There is always a need to way to export some sort of a report out to the user, previously I used the classical use of a serverside script in Java or PHP to enable user to take a screenshot of the component or the entire application. Since the flash player 10 provides the feature to save local file using file reference I am going to exploit that feature and export some BITMAP content into a Jpeg file.

following is the code for taking a snapshot of the component on some event/action

Prerequisite for making this components work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//file reference declaration to enable user to save file to local system.
private var file:FileReference = new FileReference();

//function can be called on button click or context menu click or any other interaction
private function captureIt():void{
var df : DateFormatter = new DateFormatter();
df.formatString = "DD-MM-YY HH:NN"
var bitmapData:BitmapData = new BitmapData(this.width, this.height);
bitmapData.draw(this,new Matrix());
var bitmap : Bitmap = new Bitmap(bitmapData);
var jpg:JPEGEncoder = new JPEGEncoder();
var ba:ByteArray = jpg.encode(bitmapData);
var dt : Date = new Date();
var dtStr : String = df.format(dt.time);
file.save(ba,'Screenshot at'+dt.time+'.jpg');
}

Explanation

BitmapData ()
The BitmapData () function takes a height and width of the component which has to be converted to image, i have used this you can very well use any UIComponent or DisplayObject.

bitmapData.draw(this,new Matrix ());
This is the place where the actual conversion takes place (where the bitmap data is drawn), Again you can use any UIComponent or DisplayObject in this place.

Bitmap Encoding: jpg.encode (bitmapData);
The component or screen area to be captured is converted into BITMAP data so that it could be encoded into Jpeg file format for saving it, you can also use a PNG or GIF encoder in this place as per your requirement. At this place the Bitmap data is encoded into a JPEG file format.

var ba : ByteArray = jpg.encode (bitmapData);
Since BITMAP data cannot be saved directly to the local system we first convert it into a ByteArray.

Date : dt.time
The date is only used to give each screenshot a unique timestamps so that each time the function is called a new file is saved.

file.save ( ba,’Screenshot at’+dt.time+’.jpg’ );
Now we pass the byte array to the file reference and also pass the name by which the file has to be saved, as shown in the function above.

Vollia you can now save JPEG file to your local file system via Flex without using any third party Serverside script to do so. No more HTTP request and error handling to take care of.

GO MAKE THE WORLD A BETTER PLACE
Say no to paper, No need to print graphs when you can save them to jpeg


12
May 11

Power of Flash Catalyst

Here is Kevin Lynch’s Keynote from the Web 2.0 conference where he shows how to build a full application using Illustrator, Flash Catalyst, Flex Builder, Flex and the Facebook ActionScript 3 API.


5
May 11

Save file as PDF in flex

Saving content as PDF from flex had always been a hassle, We know that doing such file operations in AIR is much simpler but due to the file saving ability added in Flash player 10 One can easily save files to the local system.

The next big question is how do i convert my content to PDF format so that it can be correctly saved into the file. Well for that folks at AlivePDF have done a gr8 job and now any one can encode their content into pdf and offer for download. They have a good documentation section which you can read and create more complex stuff.

For beginners i am writing a small example in which i will let the user download a PDF with embedded text or the Image depending on his choice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<mx:application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="522">
<mx:script>
    < ![CDATA[
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import flash.text.*;
    import flash.utils.ByteArray;

    import mx.graphics.codec.JPEGEncoder;

    import org.alivepdf.layout.Orientation;
    import org.alivepdf.layout.Size;
    import org.alivepdf.layout.Unit;
    import org.alivepdf.pdf.PDF;
    import org.alivepdf.saving.Method;

    protected var fileRefPDF:PDF;

    private function onSaveClicked(e:Event):void
    {
    fileRefPDF = new PDF(Orientation.PORTRAIT, Unit.MM, Size.LETTER);
    fileRefPDF.addPage();

    if(check.selected){
    fileRefPDF.addImage(pics);
    fileRefPDF.addMultiCell(200,5,myname.text);
    }else{
    fileRefPDF.addMultiCell(200,5,myname.text);
    }

    var bytes:ByteArray = fileRefPDF.save(Method.LOCAL);
    var file:FileReference = new FileReference();
    file.save(bytes,"Untitled.pdf");
    }



    ]]>
</mx:script>

    <mx:canvas id="pics">
        <mx:image source="@Embed('hello.jpg')" x="198" y="19" width="311" height="208"/>
        <mx:label text="Oh!! Hi," x="330" y="166"/>
        <mx:label text="{myname.text}" x="330" y="184"/>
    </mx:canvas>



    <mx:label top="33" left="14" text="Save as PDF Test "  fontWeight="bold" fontSize="15"/>    
    <mx:button click="onSaveClicked(event)" label="Save to PDF" id="saveBtn" x="14" y="148"/>
    <mx:label text="Enter your name:" y="72" x="14"/>
    <mx:textinput id="myname"  x="14" y="90"/>
    <mx:checkbox id="check" label="Save image" selected="false"  x="14" y="121"/>
</mx:application>

the Screenshot of the same can be seen below.

Note: please include the AlivePDF swc in your project.

I have just skimmed the surface of the ocean with the above code. but you can write your own brew of concoction that could possibly bring WORLD PEACE :)

(-_-) happy loafing !


11
Apr 11

Flash “like” charts for iPad and iPhone

We already know the huge gap thats been created by Apple not adopting “Flash” to run on there device. The end users and developer pay the toll as they have to adapt to this scenario and live with the limitation of either viewing their content in different visual component OR come up with a solution that works both for Flash enabled device and non Flash device like apple iPhone and iPad.

I am happy to announce that Folks at Amcharts are doing an awesome task for bridging the gap between flash users and non flash users by providing a charing component for both kinds of devices.

Now you can seamlessly view our data on both the device by using their FLASH/FLEX component or their JAVASCRIPT charting components.

How can this be done?
Use the HTML wrapper to decide if the device can handle flash or not. (can be done even with JS) Once you know what device you are running you can choose which kind of component to load. This gives you a seamless solution for your Business and YES it will support the normal flashbased platform as well as NON flash platforms

Important to note: you will have to follow the conventional MVC model to achieve. Keep VIEW and DATA separate.

 

Thanks for coming up with this solution. You guys at AmCharts ROCK!!


4
Jun 10

UI patterns

User needs: these are the UI patterns that the user uses as a tool to understand and interact with the data that has to be conveyed to the user. In short we can say these are the gadget which helps the user to understand raw data by converting them into something meaningful.  I have categorized the UI patterns in the order of “user needs and actions”.

Navigating around
· Accordion
· Headerless Menu
· Breadcrumbs
· Directory Navigation
· Doormat Navigation
· Double Tab Navigation
· Faceted Navigation
· Fly-out Menu
· Home Link
· Icon Menu
· Main Navigation
· Map Navigator
· Meta Navigation

Download detailed document: here
Continue reading →


10
Jul 09

Dynamically add remove columns in a datagrid

I had a hard time figuring out how to add remove columns  dynamically from a datagrid and after lots of code iterations I have reached a workable scenario. Right now the code is fairly easy to understand and can be enhanced to a gr8 deal,

I still need to add other important functionalities.

  • Close headerrenderer for each column
  • delete entire row functionality
  • user defined default state
  • and much more…

Dint get the time to complete this post but someday i will surely post a complete working APP
by that time check out the code  USE AND MODIFY AS PLEASED!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
< ?xml version="1.0" encoding="utf-8"?>
<mx:application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="100%" height="100%" >
   
    <mx:hdividedbox height="100%" width="100%" dividerRelease="handleDivderRelease()">
    <mx:script>
        < ![CDATA[
            import mx.controls.CheckBox;
            import mx.controls.ComboBox;
            import mx.controls.dataGridClasses.DataGridColumn;
           
            public var counter : int = 0;
            public var conDataProvider : Array =[{label : "col1", data : "c1"},
                {label : "col2", data : "c2"},
                {label : "col3", data : "c3"},
                {label : "col4", data : "c4"},
                {label : "col5", data : "c5"},
                {label : "col6", data : "c6"}];
           
           
            private function hideShowColumns():void{
                var actualColumns:Array = ResultGrid.columns;
                var actualSelectedColumns:Array =  lstColumns.selectedItems;
                var dataGridColumn:DataGridColumn;
                var sDataField:String;
                var sDataFieldCur:String;
                var columnVisible:Boolean
                for (var i:int=0;i<actualColumns.length;i++)  {
                    columnVisible = false
                    dataGridColumn = actualColumns[i];
                    sDataField = dataGridColumn.dataField;
                    for (var j : int = 0; j < actualSelectedColumns.length; j++)  {
                        if(actualSelectedColumns[j] == null) continue;
                        sDataFieldCur = actualSelectedColumns[j].data;
                        if (sDataFieldCur == sDataField)  {
                            columnVisible = true;
                            break;
                        }
                    }      
                    if (columnVisible) {
                        dataGridColumn.visible = true;
                    }else  {
                        dataGridColumn.visible = false;
                    }
                }
            }
           
            private function selectAll():void{
                lstColumns.selectedIndices=[0,1,2,3,4,5];
                hideShowColumns();
            }
           
            private function clearAll():void{
                lstColumns.selectedIndices=[];
                hideShowColumns();
            }
           
            private function setDefault():void{
                lstColumns.selectedIndices=[0,1,2,3];
                hideShowColumns();
            }
           
            private function handleDivderRelease():void{
                if(counter == 0){
                    controlBox.width =0
                }else if(counter == 1){
                    controlBox.width =120
                }
                counter = (counter +1)%2;
            }
           
        ]]>
    </mx:script>
   
    <mx:vbox id="controlBox" height="100%" width="120" maxWidth="120"  verticalGap="0" horizontalScrollPolicy="off">
        <mx:label text="Selected columns" fontWeight="bold" />
        <mx:list height="100%" width="120" id="lstColumns" dataProvider="{conDataProvider}"
                 labelField="label" borderColor="#000000"
                 allowMultipleSelection="true" click="hideShowColumns()"
                 selectedIndices="[0,1,2,3]"/>
        <mx:textarea wordWrap="true" selectable="false" editable="false" borderStyle="none"
                     text="For multiple selection use ctrl/shift keys" width="120" />
        <mx:spacer height="5" />
        <mx:button label="Select All"  width="100%" click="selectAll()"/>
        <mx:button label="Default"  width="100%" click="setDefault()"/>
        <mx:button label="Clear All" width="100%" click="clearAll()"/>
    </mx:vbox>
    <mx:vbox height="100%" width="100%">
        <mx:datagrid height="100%" id="ResultGrid" selectedIndex="0" enabled="true" selectable="true" creationComplete="init()">
            <mx:columns>
                <mx:datagridcolumn headerText="col1" dataField="c1" visible="true" />
                <mx:datagridcolumn headerText="col2" dataField="c2" visible="true" />
                <mx:datagridcolumn headerText="col3" dataField="c3" visible="true" />
                <mx:datagridcolumn headerText="col4" dataField="c4" visible="true" />
                <mx:datagridcolumn headerText="col5" dataField="c5" visible="false" />
                <mx:datagridcolumn headerText="col6" dataField="c6" visible="false" />
            </mx:columns>
        </mx:datagrid>
    </mx:vbox>
</mx:hdividedbox>
</mx:application>


25
May 09

Caching images in Flex

Caching of  image data  in your Flex application is one of the best ways to avoid loading those large images again and again, and this  can  improve performance and reduce overhead of loading external resources.

We are not discussing about using the cacheAsBitmap property to improve rendering performance or the cachePolicy property to speed up animations. Its the caching of actual bitmap data of an image. As the 2 are different in one case we just cache the image data of an image that has to be animated and component doesnot reload any image. in the other case we  reload the data on demand  for example user profile with display picture. In this case the picture mostly does not change (well it does but  we can handle it differently) so we dont need to reload the image at each time the user profile data of the same person is called.. so we can impliment a cache here.

You’ll need a hash map to store the image data. A Dictionary or an associative array will also work just fine. Loading an image for the first time is the same as usual. You create a new Image object and add a listener for the COMPLETE event or it could be any of your custom event for the sake of example we will follow :

var image : Image = new Image ();
image.addEventListener (Event.COMPLETE, onImageLoad);

Once the image has finished loading, you add a copy of the bitmap data to the hash map using the image URL as the hash key as the URL for any image will always be distinct:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private var imageCache : hashMap = new hashMap();
// create hashmap only once

private function onImageLoad (event : Event) : void{
var image : Image = event.target as Image;
var imageURL : String  = image.source;
if (! imageCache.containsKey (imageURL))
{
var bitmapData : BitmapData = new BitmapData
(image.content.width, image.content.height, true);
bitmapData.draw (image.content);
imageCache.put (imageURL, bitmapData);
}
}

The above method is called and the image is cached in the hash map. Now you can use the image as many times as we want without ever having to load it again. A good exapmle of this could be  loading user profile whenever you reload the profile data one need not fetch the  user profile picture. the picture can directly be accessed from the hashmap.

How do we use the image we stored, Its simple you need to check the hash map each time you call for the loading the image just check if you’ve already cached it:

1
2
3
4
5
// check the hashmap when ever next you want to load the image

if (imageCache.containsKey (imageURL)){
image.source = new Bitmap (imageCache.getValue (imageURL));
}

thats it now you can use the cache whenever required.


15
Oct 08

Flex dataGrid default column sort

The below example  gives you a hook to sort a  particular column in a datagrid without user input. One can use the below function where ever a datagrid has to have a default sort order on its column(s).  Note : This works with flex 2 datagrid and not with Advanced datagrid in Moxie.

The dataGridDefaultSort functions requires two input items. The dgName which as the variable suggest is the name of the DataGrid and the dgColumn, DataGrid Column, to be sorted by the numbers. column numbering begins with zero.

These input items are used with the DataGridEvent below and is dispatched (excuted). The DataGridEvent has a total of 9 values for successful operation. They are listed below in expanded detail.

1. type : String — The event type; indicates the action that caused the event. This is represented by the dgName input variable. The DataGridEvent.HEADER_RELEASE constant defines the value of the type property of the event object for a headerRelease event, which indicates that the user pressed and released the mouse on a column header
2. bubbles : Boolean (default = false) — Specifies whether the event can bubble up the display list hierarchy.
3. cancelable : Boolean (default = false) — Specifies whether the behavior associated with the event can be prevented.
4. columnIndex : int (default = -1) — The zero-based index of the column where the event occurred. This is represented by the dgColumn input variable.
5. dataField : String (default = null) — The name of the field or property in the data associated with the column.
6. rowIndex : int (default = -1) — The zero-based index of the item in the in the data provider.
7. reason : String (default = null) — The reason for an itemEditEnd event.
8. itemRenderer : IListItemRenderer (default = null) — The item renderer that is being edited or the header renderer that was clicked.
9. localX : Number — Column x-position for replaying columnStretch events.

Call this function anywhere in the program and  use as per above attributes.

1
2
3
private function dataGridDefaultSort (dgName:Object, dgColumn:int): void {
dgName.dispatchEvent(new DataGridEvent(DataGridEvent.HEADER_RELEASE,   false,true,dgColumn,null,0,null,null,0));
}