A slight update to my previous post about databinding in the MenuBar’s dataProvider.
It turns out that by just changing my initial menuData property from an Array to an ArrayCollection, everything “just works”. I guess it has to do with the ArrayCollection actually monitoring when its source changes and firing CollectionChange events. These events must then be picked up by the MenuBar parent, and it then updates its bindings. Here’s the updated code:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()" xmlns:flexoop="com.flexoop.utility.*">
<mx:Script>
<![CDATA[
import com.flexoop.utility.MenuItemWithChildren;
import com.flexoop.utility.MenuItem;
import mx.collections.ArrayCollection;
[Bindable] private var menuData:ArrayCollection = new ArrayCollection;
public function init():void {
var menuItem:MenuItemWithChildren = new MenuItemWithChildren( "test 1", true );
menuItem.children.push( new MenuItem( "test 4", true ) );
menuItem.children.push( new MenuItem( "test 5", false ) );
menuData.addItem( menuItem );
menuData.addItem( new MenuItem( "test2", true ) );
menuData.addItem( new MenuItem( "test3", false ) );
}
]]>
</mx:Script>
<mx:VBox>
<mx:ApplicationControlBar id="acb">
<mx:MenuBar id="mb" dataProvider="{ menuData }" />
</mx:ApplicationControlBar>
</mx:VBox>
</mx:Application>
Just wanted to thank Fotis Chatzinikos over on the flexcoders group for helping to solve this, at least partially. I’m still curious as to what specifically would need to change in order to switch back to an array, but at least I know the quick and easy solution.
I decided yesterday to change the name of my blog, yet again, so that I wasn’t just dropping everything into a subdomain of my family site. So, with that in mind, I decided to call the the “new” blog FlexOOP (pronounced just as it looks flex – oop, sounds similar to flex “zoop” ). A coworker of mine used to run cfoop (a ColdFusion Object Oriented site), and, while my coding may not always be the best OOP, I always strive to make it that way, thus the birth of flexOOP.com
I am not completely sure why Adobe decided not to include this in their base classes when creating the Flex framework. They have, more or less, given all of the class structure in the definition of what a menuitem must contain…enabled, toggle, groupName, etc. I decided that there’s absolutely no point in rewriting the same thing over and over again, so why not create a MenuItem class.
package com.archfamily.utility {
[Bindable]
public class MenuItem {
/***********************************
* properties
**********************************/
private var _enabled:Boolean = true;
private var _groupName:String = "";
private var _icon:Class;
private var _label:String = "";
private var _toggled:Boolean = true;
private var _type:String = "";
/***********************************
* getters
**********************************/
public function get enabled():Boolean {
return this._enabled;
}
public function get groupName():String {
return this._groupName;
}
public function get icon():Class {
return this._icon;
}
public function get label():String {
return this._label;
}
public function get toggled():Boolean {
return this._toggled;
}
public function get type():String {
return this._type;
}
/***********************************
* setters
**********************************/
public function set enabled( value:Boolean ):void {
this._enabled = value;
}
public function set groupName( value:String ):void {
this._groupName = value;
}
public function set icon( value:Class ):void {
this._icon = value;
}
public function set label( value:String ):void {
this._label = value;
}
public function set toggled( value:Boolean ):void {
this._toggled = value;
}
[Inspectable(enumeration=separator,check,radio,normal)]
public function set type( value:String ):void {
this._type = value;
}
public function MenuItem( label:String="", enabled:Boolean=true, type:String="normal", toggled:Boolean=true, groupName:String="", icon:Class=null ) {
this.enabled = enabled;
this.groupName = groupName;
this.icon = icon;
this.label = label;
this.toggled = toggled;
this.type = type;
}
}
}
I added the properties to the constructor so that the menu items could be created without have to first create a variable for them, but could rather just do something like “myArray.push( new MenuItem( “myLabel”, true ) )”. Initially I added the “children” as a property of this class, but then I found out that this will cause the menu items to show an arrow next to their name as if the MenuItem actually had children (even though the item is an empty array). In order to circumvent this problem, I decided to create a second class, MenuItemWithChildren, that extends MenuItem, but adds the property “children”:
package com.archfamily.utility {
public class MenuItemWithChildren extends MenuItem {
/***********************************
* properties
**********************************/
private var _children:Array = [];
/***********************************
* getters
**********************************/
public function get children():Array {
return this._children;
}
/***********************************
* setters
**********************************/
public function set children( value:Array ):void {
this._children = value;
}
public function MenuItemWithChildren(label:String="", enabled:Boolean=true, type:String="normal", toggled:Boolean=true, groupName:String="", icon:Class=null) {
super(label, enabled, type, toggled, groupName, icon);
}
}
}
Now, whenever I want to create a menu item within one of my menu bars, all I have to do is create an array that holds all of my MenuItem or MenuItemWithChildren classes, and I have strongly typed class rather than a plain, old array of generic objects. The one other thing to note is that if the menu is not created in the property declarations, then you have to set the array to the dataProvider of the menu item, or it will not refresh the view and you will not be able to see your menu items. The implementation would look something like this:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()" xmlns:archfamily="com.archfamily.utility.*">
<mx:Script>
<![CDATA[
import com.archfamily.utility.MenuItemWithChildren;
import com.archfamily.utility.MenuItem;
[Bindable] private var menuData:Array = [];
public function init():void {
var menuItem:MenuItemWithChildren = new MenuItemWithChildren( "test 1", true );
menuItem.children.push( new MenuItem( "test 4", true ) );
menuItem.children.push( new MenuItem( "test 5", false ) );
menuData.push( menuItem );
menuData.push( new MenuItem( "test2", true ) );
menuData.push( new MenuItem( "test3", false ) );
mb.dataProvider = menuData;
}
]]>
</mx:Script>
<mx:VBox>
<mx:ApplicationControlBar id="acb">
<mx:MenuBar id="mb" dataProvider="{ menuData }" />
</mx:ApplicationControlBar>
</mx:VBox>
</mx:Application>
I haven’t had to use the Menu class very often yet, but I’m sure this will come in handy to someone else. If nothing else, it saves typing in Flex Builder.
Enjoy!
Building off of my last post, I thought I might as well create a deduped arraycollection as well, just in case anyone wanted to use the data for something other than a combobox. The implementation for this component is very similar to that of the combobox. The DedupeArrayCollection allows you to pass it an array (one that has some kind of properties to each of the items within it), then set the dedupeProperty to whatever field you wish to dedupe.
The DedupeArrayCollection:
package com.archfamily {
import flash.events.Event;
import flash.utils.Dictionary;
import mx.collections.ArrayCollection;
import mx.events.CollectionEvent;
public class DedupeArrayCollection extends ArrayCollection {
private var _dedupeProperty:String = "";
public function set dedupeProperty( value:String ):void {
_dedupeProperty = value;
this.source = this.source;
}
public function get dedupeProperty():String {
return _dedupeProperty;
}
/**
*
* Needed to override the standard dataprovider in order to reset
* the duplicate value each time
*
*/
override public function set source( value:Array ):void {
var _returnArray:Array = value;
if ( value && dedupeProperty.length > 0 ) {
var _map:Dictionary = new Dictionary( true );
value.forEach( function( item:*, index:int, array:Array ):void {
_map[ item[ this ] ] = item; // in the loop, this == dedupeProperty
}, dedupeProperty );
_returnArray = [];
for each ( var object:Object in _map ) {
_returnArray.push( object );
}
}
super.source = _returnArray;
}
public function DedupeArrayCollection( source:Array=null ) {
super( source );
}
}
}
The implementation of the arraycollection:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()" xmlns:archfamily="com.archfamily.*">
<mx:Script>
<![CDATA[
import com.archfamily.DedupeArrayCollection;
[Bindable] private var ac:DedupeArrayCollection = new DedupeArrayCollection( [ { name: "car", total: 100 },{ name: "bus", total: 200 },{ name: "bike", total: 300 },{ name: "train", total: 200 },{ name: "boat", total: 400 },{ name: "car", total: 500 },{ name: "bus", total: 200 },{ name: "convertible", total: 100 },{ name: "gizmo", total: 300 },{ name: "boat", total: 300 },{ name: "car", total: 100 },{ name: "dune buggy", total:300 },{ name: "motorcycle", total: 100 }] );
public function init():void {
ac.dedupeProperty = "total";
}
]]>
</mx:Script>
<mx:VBox>
<mx:ComboBox id="testing" dataProvider="{ ac }" labelField="name" />
</mx:VBox>
</mx:Application>
The only thing I would probably change is to figure out what event needs to fire when my dedupeProperty is set to get the arraycollection to reload the source. Currently I’m just setting the source to itself so that the event fires and reloads the arraycollection, but just firing the event would probably be less overhead.
There are times that I have wanted to remove all duplicates from a combobox’s dataprovider, but haven’t wanted to requery the database or roll my own code snippet each time to remove those duplicates. To make my life a little easier (and hopefully anyone else that may be in need of this feature), I’ve created the DedupeComboBox class.
This handy little item allows you to set the dataprovider of the combobox to an array or arraycollection, and set the labelField value and the combobox will then handle the rest. The labelfield is important as that is what allows the component to remove the duplicates from the arraycollection (if it is an array, it must also be an array of objects with properties that match the labelField or it will not remove anything). Also, as the items populate the deduped dictionary instance, the value of that item in the arraycollection is saved to that “key” in the dictionary, allowing for retrieval of it later on if necessary.
package com.archfamily {
import flash.utils.Dictionary;
import mx.collections.ArrayCollection;
import mx.controls.ComboBox;
public class DedupeComboBox extends ComboBox {
/**
*
* Needed to override the standard dataprovider in order to reset
* the duplicate value each time
*
*/
override public function set dataProvider( value:Object ):void {
var _localArray:Array = ( value is ArrayCollection ) ? ( value as ArrayCollection ).source : value as Array;
var _map:Dictionary = new Dictionary( true );
if ( labelField.length > 0 ) {
_localArray.forEach( function( item:*, index:int, array:Array ):void {
_map[ item[ this ] ] = item; // in the loop, this == labelField
}, labelField );
}
var _returnArray:Array = [];
for each ( var object:Object in _map ) {
_returnArray.push( object );
}
super.dataProvider = _returnArray;
}
public function DedupeComboBox() {
super();
}
}
}
then to implement it from your code you could do something as simple as this
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:archfamily="com.archfamily.*">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable] private var ac:ArrayCollection = new ArrayCollection([ { name: "car", total: 100 },{ name: "bus", total: 200 },{ name: "bike", total: 300 },{ name: "train", total: 200 },{ name: "boat", total: 400 },{ name: "car", total: 500 },{ name: "bus", total: 200 },{ name: "convertible", total: 100 },{ name: "gizmo", total: 300 },{ name: "boat", total: 300 },{ name: "car", total: 100 },{ name: "dune buggy", total:300 },{ name: "motorcycle", total: 100 }]);
]]>
</mx:Script>
<archfamily:DedupeComboBox id="testing" dataProvider="{ ac }" labelField="name" />
</mx:Application>
You can even get just the non-duplicate totals by changing labelField=”name” to labelField=”total”.
I’m always looking for ways to make my life a little easier when writing code, and to take the monotony out of the equation wherever I can. I know there is lots of back-and-forth about whether this method should or should not be used in ColdFusion, but I feel that for the minor incovenience of running a reg ex, creating getters and setters in my CFCs/Objects just make sense if just for the very fact of encapsulation. Plus if you need to alter anything that occurs during a “set” operation, nothing outside of the object is broken when you make the change within your object. Anyway, back to business:
This regular expression can be used in Eclipse (my current IDE) and will convert something that looks like:
<cfset variables.firstName = "" />
<cfset variables.lastName = "" />
into
<cffunction name="getfirstName" output="false" access="public" returntype="any">
<cfreturn variables.firstName />
</cffunction>
<cffunction name="setfirstName" output="false" access="public" returntype="void">
<cfargument name="val" required="true" />
<cfset variables.firstName = arguments.val />
</cffunction>
The search string is:
<cfset variables.(\w+) = [^>]+>(\r\n)(\t)
and the replace string is:
<cffunction name="get$1" output="false" access="public" returntype="any">$2$3$3<cfreturn variables.$1 />$2$3</cffunction>$2$2$3<cffunction name="set$1" output="false" access="public" returntype="void">$2$3$3<cfargument name="val" required="true" />$2$3$3<cfset variables.$1 = arguments.val />$2$3</cffunction>$2$2$
The search string contains a “tab” character at the end. This is really just so I can format the output of the getters and setters, but feel free to modify them however you wish.
I know I may not be the best with Eclipse, but some of the notes that Google is giving for using Eclipse and their tutorials need to be updated. As I progress through the tutorials, I’ll probably be adding new updates to assist others with problems I come across (not that there are many, but could cause other beginners to Android give up before even getting a good start).
The first thing I came across that was mildly annoying, but needs to be noted as it may cause some issues for others trying to follow along with the tutorials. In Tutorial: Notepad Exercise 1, Step 1, it mentions that to create the new project to download the source and select “Create project from an existing source”. When I tried to do this, Eclipse balked and stated that there already existed. I then moved my files from my workspace to my desktop then tried again. However, using this method, the project was now using the files from my desktop.
I think the better way to get the project up and running is to import the files (as Eclipse actually states when you try to create the project in the same space as your workspace). To do this go to
File -> Import -> General -> Existing Projects Into Workspace
Then select “Browse” next to “Select root directory” and find your Notepad files (mine were on my desktop).
Make sure to also check “Copy projects into workspace” so the files are copied to your android workspace.
Click Finish.
Now you should have the Notepadv1 project imported into your android workspace in Eclipse and all files should be located with the rest of your android files. This to me seems like a better way of accessing the files than having them all over your computer.
With all of the buzz about Android (Google’s foray into an OS for mobile devices), I decided that now would be a good a time as any to actually check out the code, and see what I could come up with. The code is actually very similar to Flex, probably due to it being written in Java, so I was able to come up to speed pretty quickly once I began to look into it. The only thing that is taking a little bit of time is knowing what all of the libraries are that comprise the Android code base. Luckily Google has planned for that eventuality and provided a very useful API (as they do with all of their distributed code).
So far I have only downloaded the “Hello World” application (or Hello Android in this case), but that ran without too many issues. The main one that I came across (and the reason for naming this article the way I did), was that each time I ran the Android emulator, my C drive lost about 40 megs of hard drive space. Not realizing this at first, I actually ran out of hard drive space while I was testing.
After some digging, it turns out that the emulator creates a ~41MB temp file on the local drive each time the emulator is run that is Temporary System image file. My only problem is that this is not removed each time you test the device, and, after a while, as I found out, the hard drive will get filled up until those files are deleted. Even finding them was a bit of a pain, but in order to alleviate that pain for others go to
C:\documents and settings\<logged in user>\Local Settings\Temp\AndroidEmulator\
and delete all files that you find in there. This will remove all of the temporary system files that are created each time Android starts up, and save your hard drive space.
If I get time I may start a “how-to” series for Android, so others can see what’s needed to write Android apps. I’m enjoying it so far…perhaps I’ll even buy myself a G1 at some point