vendredi 15 juillet 2011

recolte du 24.07.11

Inocybe phaeodisca

Russula À cause du stipe fortement rosé, j'ai un douté si c'est une Aurora, pour sa taille, ou une lilacea.

vendredi 19 juin 2009

How to customize a cell in a datagrid with AS3 : Part 2

In the previous ticket, How to customize a cell in a datagrid with AS3, I explained how to change the background color of a cell. Now, let see how to add a datagrid that contains various components like checkboxes and buttons. I will take the same data model:
Name$
Apple0.5
Bread1.99
Asparagus3.0
Banana0.8
But in addition of those two columns, I want to add a checkbox and , let's say, an input field. And the checkbox represents which groceries the user want to buy, with the input field indicating the quantity. How to do that? This can be done in 2 steps and 2 classes. And the UILoader component. Let's start again with the cell renderer. Instead of extending the cellrenderer, it will this time be extending UILoader. This will allow to put any kind of component or movieclip inside the cell, which makes it reusable. However, because I could only get the whole row values but not the current cell value, I have to create a cell renderer for each column. But there is only one field changing from one cell renderer to another. So I post the template there:
package {
 import fl.containers.UILoader;
 import fl.controls.listClasses.ICellRenderer;
 import fl.controls.listClasses.ListData;
 import fl.core.InvalidationType;

 public class LoaderCellRenderer extends UILoader implements ICellRenderer {
     protected var _data:Object;
     protected var _listData:ListData;
     protected var _selected:Boolean;
  
     public function LoaderCellRenderer():void {
         super();
     }
  
     public function get data():Object {
         return _data;
     }
     public function set data(value:Object):void {
         _data = value;
         source = value.Select;
     }
  
     public function get listData():ListData {
         return _listData;
     }
     public function set listData(value:ListData):void {
         _listData = value;
         invalidate(InvalidationType.DATA);
         invalidate(InvalidationType.STATE);
     }
  
     public function get selected():Boolean {
         return _selected;
     }
     public function set selected(value:Boolean):void {
         _selected = value;
         invalidate(InvalidationType.STATE);
     }
  
     public function setMouseState(state:String):void {
     }
 }
}
In bold, you can see the name of the column which provides the movieclip to render. In this example, the cell renderer is using the content of the data of each row at the column Select. In the data, instead of storing a text or number, I'll store a checkbox object that the UILoader will use. And so is the trick. For this demo, I've also created a copy of this class for the column Quantity, which will contain the input field. In the second step, I need to configure the datagrid columns and to add data properly: To configure columns:
var SelectColumn:DataGridColumn = new DataGridColumn("Select");
SelectColumn.cellRenderer = LoaderCellRenderer;
SelectColumn.width = 25;

var QtyColumn:DataGridColumn = new DataGridColumn("Quantity");
QtyColumn.cellRenderer = LoaderCellRendererInput;

dataGrid.addColumn(SelectColumn);
dataGrid.addColumn(QtyColumn); 
dataGrid.addColumn("Name");
dataGrid.addColumn("Price");
Now the datagrid is ready. You can add data in its dataprovider like that:
var c:Checkbox=new Checkbox();
c.text="";
var input:TextInput=new TextInput();
reportDataGrid.addItem({Select:c, Quantity:input, Name:"Apple",Price:0.5}); 
I have now a row with a checkbox, a textinput and the usual textfields Apple and 0.5. It's possible to populate the list and have to look that I wanted. But it's pretty much useless if the components do nothing. Sure, with the dataprovider we know if the checkbox of a certain line is checked, or we can read the value of a specific textinput. But sometimes, we want to trigger an event from a component in one of those rows. Suppose we want to check the checkbox of a row when we enter a quantity. To do that, we have to create a class that extends the textinput, because the component needs to know wich checkbox to change and the original textinput is missing a field for that. So, I simply create a CustomTextInput that extends TextInput and that has a public var of type Checkbox by default null. Then, to add data:
var c:Checkbox=new Checkbox();
c.text="";
var input:CustomTextInput=new CustomTextInput();
input.checkbox=c;
input.addEventListener(Event.CHANGE,onQtyChange);
reportDataGrid.addItem({Select:c, Quantity:input, Name:"Apple",Price:0.5}); 


function onQtyChange(e:Event){
  var input:CustomTextInput=CustomTextInput(e.currentTarget);
  input.checkbox.selected=true;
}
And so, the checkbox of each row will be affected by the input field changes in the same row.

jeudi 18 juin 2009

How to customize a cell in a datagrid with AS3

In Flash 9, with the introduction of AS3, many components from AS2 were rewritten. But, probably due to a lack of time, Adobe dev team rewrote only some components and with less features. Well, the jump between the old and riche AS2 and the freshly baked AS3 was high anyway. I didn't use AS2 and I jumped in flash 9 with AS3, mostly because I'm a developer and not a designer, and AS3 has an object oriented structure more fitted for me. Anyway, one thing that caused me big troubles was the List and Datagrid components. Simply put, they're hard to customize without breaking the whole component. For instance, changing a List in a way it displays a movieclip with a text and checkbox instead of a standard text looks nearly impossible to do. At least, that was the feeling when I was reading the documentation from Adobe. And in a first time, because my boss want to see progression in the project, I had to reimplement a list, which was also painful, introducing bugs and lacking a lot of features. But with time, and starting to work with datagrids, I could find little by little a way to customize the datagrid with the help of ICellRenderer. Just a note about ICellRenderer. This abstract class has the mean to allow the developer to implement his own cellrenderer for lists, comboboxes and datagrids. However, the way to implement it is not documented and far from being obvious. My best luck was to google some working examples, and I ended up with to working ways of customizing a Datagrid.

How to change the background color per row value Suppose you want to color a datagrid depending on the value of a cell. In fact, suppose you have a list of items and their price like that:
Name$
Apple0.5
Bread1.99
Asparagus3.0
Banana0.8
And suppose you want to color any row in red where the price is over $1 (because it's expensive). You will need at least 2 classes:
  • a cell renderer
  • a shape that will be the red background of the cell
Let's see the cell renderer:
package {
import fl.controls.listClasses.CellRenderer;
import fl.controls.listClasses.ICellRenderer;

public class CustomCellRenderer extends CellRenderer  
  implements ICellRenderer {

public function CustomCellRenderer ():void {
super();
}

public static function getStyleDefinition():Object {
return CellRenderer.getStyleDefinition();
}

override protected function drawBackground():void {
if (_data)
if (_data.price> 1) { setStyle("upSkin", RedColor ); }
super.drawBackground();
}
}
}
You can see (in bold) that I use the column "price" of the row stored in _data to do the condition to show the color. It means that you must have a column called "price" in your data to be able to use this cell renderer. Let's see now the shape class:

package
{

import flash.display.Sprite;

public class RedColor extends Sprite

{
private var _rectangle:Sprite;
public function RedColor()
{
var _rectangle = new Sprite();
_rectangle.graphics.beginFill(0xff0000,1);
_rectangle.graphics.drawRect(0, 0, 100, 20);
_rectangle.graphics.endFill();
addChild(_rectangle);
}
}
}
The class is very simple but it can't be generic because we can't manage the instances of the class. So, I didn't find a way to set the color in the runtime and I had to create one class per color. To use those classes, simply add a column to the datagrid like that:
var nameColumn:DataGridColumn = new DataGridColumn("Name");
nameColumn.cellRenderer = CustomCellRenderer ;
_grid.addColumn(nameColumn);
This will add a column in the datagrid which will display the value name with a background red or normal depends on the value of the column price. Next time, I'll explain how to add a check box, button or movieclip in a cell and add them a custom behavior.

jeudi 11 juin 2009

The timeline is still your best friend

Referring to the previous post, a flash application that needs to be initialized at the beginning, and furthermore which must be called by javascript at the beginning, there is at least one guaranteed way to know when a flash movie is ready: the timeline. Once the scripts in the timeline are being executed, we know for sure the flash application is ready to be used by javascript or to manipulate lists. This means, supposing your flash app has a class document, you have to create an initializing function (e.g. init()) that is called in the very first frame of the timeline (but which must be called only one time. This must be taken care of). And if javascript is involved, the init() can do an ExternalInterface.call to tell to javascript the flash file is ready.

mardi 12 mai 2009

Thou shall not touch Lists at the Beginning

As strange as it looks like, if you try to access to the dataProvider of a List created in flash from the constructor of a class associated to a movieclip that owns the list, you'll get a bug. In fact, it seems that the dataProvider of the list object is initialized after the constructor of the parent movieclip is executed, meaning that whatever you do with the list or its dataProvider in the constructor will not be shown. Very simple example:
  1. in flash 9, create a new movieclip and add to it a List component.
  2. create a classe for the movieclip.
  3. add in the class a constructor with the following content: myList.dataProvider.addItem({label:"test",data:0});
  4. run it and expect to see a list with a line "test". But because of some kind of bug, nothing appears in the list.
However, if you do the addItem from a button click event, it works perfectly fine.

lundi 11 mai 2009

The List won't listen

Once again I'm fighting with the component List and its dataProvider. For some reason it refuses to do the simplest update when I do a removeAll() or an AddItem() if the script is executed inside a class of the parent movieclip. This just doesn't make sense.