Rambling Developer

Recent Posts


Archives


Rambling Developer

Rambling Developer


February, 2011 5

Flex Memory Management Question

One of the best questions to ask during an interview for a new Flex developer is how to handle memory management in Flex.  You can immediately gauge someone’s competence (how they handle memory issues) and experience (if they’ve had to handle memory issues on large projects) with this single question.

If someone fails to answer this question adequately, it’s an immediate red flag that the interviewee probably isn’t hiring material.

A good answer involves weakreferences, methods to cleanup or dispose of all references to a class/object, reusable renderers and sometimes using Grant Skinner’s Janitor class.

Grant Skinner has also published an good overview presentation on memory management here.

adminadmin

Flex Java removeAll() Implementation

Just created a implementation of the Java removeAll() method for Flex ArrayCollections.  Here it is:


public function removeAll(collection1:ArrayCollection,
                    collection2:ArrayCollection):ArrayCollection{
  var dictionary : Dictionary = new Dictionary(true);
  var value : Object;
  var i:Number
  //Loop through first collection and put objects in dictionary
  for(i = 0; i < collection1.length; i++){
    value = collection1.getItemAt(i);
    dictionary[value.id] = value;
  }
  //Loop through second collection and remove objects in dictionary
  for(i = 0; i < collection2.length; i++){
    value = collection2.getItemAt(i);
    if(dictionary[value.id] != null){
      delete dictionary[value.id];
    }
  }
                     
  var unique:ArrayCollection = new ArrayCollection();
  //Loop through dictionary and put remaining value in collection
  for(var prop:String in dictionary){
    unique.addItem(dictionary[prop]);
  }
  dictionary = null;
  return unique;
}

Here's a link to the Adobe Forum post where I created this snippet: http://forums.adobe.com/thread/793088

adminadmin

ComboBox ItemRenderer Cook Book

One of the first things that is created or envisioned before the need for a DataGrid and ComboBox itemRender is a collecton of data. Some example data that will be used for this cook book is shown below:

<s:ArrayCollection id="usStatesCollection" source="{[Alabama,Georgia,Massachusetts,New York,Rhode Island]}"/>
<s:ArrayCollection id="userCollection">
    <fx:Object userName="mikejuly24" usState="Massachusetts"/>
    <fx:Object userName="leonthepro" usState="Alabama"/>
    <fx:Object userName="mathilda" usState="New York"/>
</s:ArrayCollection>

Notice above that two ArrayCollection have been created. One is a collection of my favorite US States and the other is a collection of Objects that map a userName to a usState.

Now, we will want to create a Datagrid with two columns that can display this information. Our first column will display a userName as a Label and the second column will display the usState in a ComboBox. The usState column renderer/editor will be a ComboBox so that the end user can ultimately change the US State that is mapped to each userName. Below we create our DataGrid in MXML:

<mx:DataGrid id="myDataGrid" dataProvider="{userCollection}">
  <mx:columns>
    <mx:DataGridColumn headerText="User Name" dataField="userName" editable="false" width="200"/>
    <mx:DataGridColumn headerText="Home State" rendererIsEditor="true"editable="true" dataField="usState" width="200">
      <mx:itemRenderer>
        <fx:Component>
          <mx:ComboBox dataProvider="{outerDocument.usStatesCollection}" change="{outerDocument.comboBoxChanged(event)}"/>
        </fx:Component>
      </mx:itemRenderer>
    </mx:DataGridColumn>
  </mx:columns>
</mx:DataGrid>
<s:Label id="updateLabel" x="425" y="10" text="No Changes Have Been Made Yet."/>

Notice several important points in the above code:

  1. The dataGrid is bound to the userCollection so that all changes to the userCollection will be reflected in the DataGrid.
  2. We have defined a column for userName which displays the userName attribute with dataField=”userName” and sets the column header text to “User Name”.
  3. We have created a column which uses “usState” as its dataField, sets up the renderer to be the editor, and allows the renderer to be editable.
  4. We define an itemRenderer Component for the “usState” column which is comprised of a ComboBox which uses the usStateCollection ArrayCollection as the dataProvider and calls a comboBoxChanged() method when the associated data changes.
  5. Lastly, we create a label with id of updateLabel which we will use to display changes that are made to the ComboBoxes.

Having the ComboBox renderer call a method upon change is important since this will allow us to detect and handle changes which are made to each individual ComboBox renderer.  Here we define the comboBoxChanged() method:


public function comboBoxChanged(e:Event):void{
  //Create a string denoting the newly selected state along with the related username
  var updateString:String = "Selected state is: " + ComboBox(e.currentTarget).selectedItem +" for user: " + ComboBox(e.currentTarget).data.userName;
  //Print out the newly selected state along with the related username
  trace(updateString);
  //Display the newly selected state along with the related username in the UI label
  updateLabel.text = updateString;
}

In the above method, we are first creating a string which will represent the change made by using the information passed into the method by the Change Event. The passed in event contains the newly selected State in the selectedItem attribute and also contains the data for the row (ie. the Object in the usersCollection). Once the string is created using the information retireved from the event, we use trace() to output the string and set the text of the updateLabel to the string.

This concludes all of the logic required to retrieve the data changes which are being made to the comboboxes, but we may also want to programmatically change the values displayed by the ComboBoxes. To achieve that goal, we will need to add an input source to allow programatic changes to the Boxes, like so:

<s:Button label="Make Everyone From Georgia" click="setAllUsersToGeorgia()" x="425" y="50"/>

As the label of the button aptly describes, the button will be used to switch all of the users in the DataGrid to be from Georgia. To do that, we register a method called setAllUsersTOGeorgia() to the click event which is implemented below:


public function setAllUsersToGeorgia():void{
  //Loop through our user collection and set every users state to Georgia
  for each (var user:Object in userCollection) {
    user.usState = "Georgia";
  }
  //Refresh the grids dataprovider so that the renderers update with the new data
  myDataGrid.dataProvider.refresh();
}

We can see in this method, that we are looping through the userCollection and changing all of the Users states to be “Georgia”.  We then ensure that the DataGrid updates all of its renderers correctly by calling the refesh() method on the dataProvider.  

Thats it!  That is everything we need to do in order to get and set values from a ComboBox in a DataGrid!

To view a demo of this code go to: http://ramblingdeveloper.com/storage/flex-examples/datagridcomboboxexample/bin-debug/DataGridComboBoxExample.html

To get the source to the demo: http://ramblingdeveloper.com/storage/flex-examples/datagridcomboboxexample/DataGridComboBoxExample.mxml

adminadmin

Flex Dictionary Serialization

Just ran across an interesting issue with the serialization of Dictionaries to Java when targeting Flash 9 as opposed to Flash 10.

When targeting Flash 9, a remote object call, which passes a Dictionary as a parameter, would be correctly serialized and then deserialized as a Map on the Java side. This is not true with Flash 10!

When targeting Flash 10, a remote object call, which passes a Dictionary as a parameter, would NOT be correctly serialized and then deserialized as a Map on the Java side.

To be fair, it seems that Adobe has never officially supported serializing the Dictionary class and has recommended using an Object instead. So, to solve my problems, instead of sending a Dictionary as a parameter to my remote object calls, I now send an Object.

To be clear, I have switched the creation of my Dictionary from:

<p>var dictionary:Dictionary = new Dictionary();</p>
<p>dictionary["one"] = 1;</p>
<p>dictionary["two"] = 2;</p>

to:

<p>var object:Object = new Object();</p>
<p>object["one"] = 1;</p>
<p>object["two"] = 2;</p>

With that simple change everything serializes correctly again and I can correctly pass Maps to and from the server.

Lesson learned: Never use something that is not officially supported by Adobe and then expect it to work correctly when targeting new versions of the Flash Player!

adminadmin