Monday, February 22, 2010

When to use what Spark Container

As always, I'm disappointed with the amount I blog. One strategy I'm going to take is to take questions asked on forums that I think are interesting and provide an answer. So, for this first question comes from a forum post.

Question: We know that Flex 4 has all this new container as Group, VGroup, HGroup, etc...as well as the old Halo componentas as the Canvas, Panel, VBox, HBox, etc...

This gets me really confused on which container i should use with my project.

For example, what's the difference between:

<s:Group>
    <s:layout>
        <s:HorizontalLayout />
    </s:layout>
</s:Group>

...and...

<s:HGroup>
</s:HGroup>

...or even...

<mx:HBox>
</mx:HBox>

I'm sorry if i'm saying something wrong but it makes me waste so much time thinking on which one i should use cause, for me, all of them do exactly the same.

One other thing is if i need to do a container with a background color. What should i use?

<mx:Canvas backgroundcolor="{color}">
    <MyComponents />
</mx:Canvas>

...or...

<s:Group>
     <s:Rect>
        <s:fill>
            <s:SolidColor color="{color}" />
        </s:fill>
    </s:Rect>
    <MyComponents />
</s:Group>

Can anyone tell me which one is the best pratice now for Flex 4?

Answer:

We hope you can use the new Spark components whenever possible. Group is the basic, chromeless container. SkinnableContainer is our basic container with chrome (for instance Panel extends SkinnableContainer). One of the cool new things you can do in spark is swap layouts. For instance, you can use a HorizontalLayout with a Group, or you can even create a custom layout and use it with your List. This is really powerful, and this separation between a container and the layout make a lot of things easier, That said, for really common use-cases, we found some people were annoyed with having to type:

<s:Group>
    <s:layout>
        <s:VerticalLayout horizontAlign=".." />
    </s:layout>
     ...
</s:Group>

And people were used to having HBox and VBox. Because of this, we created helper classes, HGroup and VGroup, which extend Group and have the layout object baked in. They don't really add any functionality...they are just there as a shortcut. To make it easier we also proxy up some layout-dependent properties on to the container, like horizontalAlign and verticalAlign.

In summary:

HBox, VBox, and Canvas are replaced by one component, Group. However their functionality is really replaced by HorizontalLayout, VerticalLayout, and BasicLayout, respectively. We have helper objects HGroup and VGroup to make common tasks easier. If you want a skinnable form of those components (something with chrome), use SkinnableContainer and change the layout (we did not provide helper classes HSkinnableContainer and VSkinnableContainer because we don't think it's as common of a use-case).

As a side note, I think you should always switch to using these new Spark container classes whenever possible. We think they are more flexible because of swappable layouts, and they also support GraphicElements directly, while MX containers do not. In addition, the Spark classes are made to be lighter-weight and more performant. For instance, they don't have scrollbars built in, and they are chromeless. An exception is if you used Canvas and advanced constraints before. We haven't created an AdvancedConstraint layout yet.

As per the backgroundColor part of your question, you could composite it together like you did in the Group:

<s:Group>
     <s:Rect>
        <s:fill>
            <s:SolidColor color="{color}" />
        </s:fill>
    </s:Rect>
    <MyComponents />
</s:Group>

However, ideally, I'd tell you to use SkinnableContainer and place the Rectangle in the SkinnableContainer's skin. This way you're segregating the content of the component from the look of the component. This is part of the "container with chrome (SkinnableContainer) vs. container with no chrome (Group)". As we started out down the Spark architecture path, we really pushed hard for people to use this new skinning model, and in this case, we would push people to put the <s:Rect> in to the skin, but in balancing our "theoretical ideal of the skin vs. the component" and the usability of the framework, we received feedback that for common situations we need to provide styles. Because of this, our default SkinnableContainer skin already has a background rectangle in it, and you can control its color with the backgroundColor style, like:

<s:SkinnableContainer backgroundcolor="red">
    ...
</s:SkinnableContainer>

We baked this background and backgroundColor style in to the SkinnableContainer component for ease-of-use, so you don't have to create a skin just for simple changes. However, if you want something really complex (like a rounded rectangle as the background, then you should re-skin the SkinnableContainer).

Hope this helps answer a lot of questions.

-Ryan

Friday, March 27, 2009

BugQuash

There's an awesome event going on tomorrow--BugQuash. The idea is simple: get together with a bunch of other Flex developers and work on fixing bugs in the SDK. I think this is good for two reasons. It'll help fix a bunch of bugs, and it help create a better community around the SDK. I really, really wish I could attend tomorrow, but I can't because of some other obligations. However, I'll try to come back and be online for part of it towards the end, plus there'll be some other Flex SDK engineers available during the bugquash. The event goes from Sat., March 28, 2009 10 am - 8 pm PST, and it's definitely worth checking out.

Sunday, December 14, 2008

Flex Videos on your iPod

Our Flex videos are becoming more popular. Because of this, we've decided to make them available for your iPod. I think we'll be publishing more, but for now some of our more popular ones are up there. One of mine is up there--the one about building SWC files. Here's a link to the videos: http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=299639895

Wednesday, November 19, 2008

MAX 2008: Item Renderer Talk

MAX has been a lot of fun so far this year. Joan and I gave a talk at MAX|360 on item renderers. It was a decent crowd for the 360 area, and we got a lot of questions, which was awesome. I think the presentation went really well. There was only one goof in the code, and I've fixed that in this posting (the item renderer code was fine--I just forgot to set editable="true" in the DataGrid for RatingExample4.mxml). We had to speed through in the end and didn't get to talk too much about the star rating renderer or some of the gumbo stuff, but we did get to answer a lot of people's questions, which is what's important. We don't have too much information explaining the code and what slide it accompanies, but if you guys have questions, let me know. There's a lot of good stuff in there, including how to implement IDropInListItemRenderer, using states in renderers, using a checkbox as an editor, and a "star rating renderer," which I hope people can use. Here's the last example we didn't get to show. Anyways, take a look at the slides & examples, and let me know if you've got any questions. Slides (PDF) | Slides (PPTX) Examples

Monday, November 17, 2008

MAX 2008

I'm lucky this year because MAX is in my backyard--San Francisco. I'm putting some last minute touches on a presentation I've been working on. I haven't given talks to a real Flex crowd before, and I figured MAX|360 would be a great start. Joan and I are giving a talk on item renderers. It should cover a lot of material, and it'll hopefully cover everything you need to know about item renderers. It's at 1:30 on Tuesday in the MAX|360 room. I know it's got some stiff competition, since Deepa's talk is at the same time, but it should be a useful session, and I hope you'll stop by. If you can't make it, flag me down to say hello at the conference.

Tuesday, October 14, 2008

SWCs, Modules, and RSLs

So a month or two ago, I helped out with some training videos for "Flex in a Week," which I think is a great program and a great way to learn Flex. The videos aren't perfect, but given that I was nervous and really only had one shot, they're not too bad :-) You can find the videos under Day 5 in the series: http://www.adobe.com/devnet/flex/videotraining/ The source code for the demos can be found here:

Monday, July 21, 2008

Creating a Frameworkless MXML File Part 2 (Having Children)

In our last post, we saw how to use MXML (and Flex Builder) to instantiate one top-level object. However, to actually be useful, let’s learn how to add some children. There are two main ways to do this, through IMXMLObject and through default properties. Both are pretty easy to understand. Looking at the IMXMLObject interface, you’ll see one method:
package mx.core
{

/**
 *  The IMXMLObject interface defines the APIs that a non-visual component
 *  must implement in order to work properly with the MXML compiler.
 *  Currently, the only supported method is the initialized()
 *  method.
 */
public interface IMXMLObject
{
    //--------------------------------------------------------------------------
    //  Methods
    //--------------------------------------------------------------------------

    /**
     *  Called after the implementing object has been created and all
     *  component properties specified on the MXML tag have been initialized.
     *
     *  @param document The MXML document that created this object.
     *
     *  @param id The identifier used by document to refer
     *  to this object.
     *  If the object is a deep property on document,
     *  id is null.
     */
    function initialized(document:Object, id:String):void;
}
}
If your child object implements this interface, the MXML compiler will generate code to automatically call this method. It’s a pretty limited way to add children because it only has two properties, document and id. It actually doesn’t tell you who your parent is--just your document, which is your top-level root node object (usually Application). Sticking with stupid-simple examples, I’m just going to draw some ellipses and rectangles. The code for drawing shapes has been ripped off of my co-workers, Chet Haase, and modified to make it as simple as possible and fit the example.
<?xml version="1.0" encoding="utf-8"?>
<components:MyContainer 
    xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:components="components.*" 
    xmlns:shapes="components.shapes.*">
    
    <shapes:Rect color="0xFF0000" startX="20" startY="20" endX="100" endY="30" />
    <shapes:Ellipse color="0x00FF00" startX="20" startY="120" endX="100" endY="130" />
</components:MyContainer>
Rather than post all the code here, I’ll just post some snippets (full code is in two flex builder projects: IMXMLObject example and the Default property example). The interesting part is ArtShape, which is the base class for all shapes. That class is the one that implements IMXMLObject, and all it does is:
public function initialized(document:Object, id:String):void
{
    document.addChild(this);
    renderShape();
}
So basically this initialized method just adds ourselves to the top-level application and calls a method that Chet had called renderShape(). The ArtShape version of that method is empty, but Rectangle’s looks like:
/**
 * Draws a rectangle into this shape's display list
 */
override protected function renderShape():void
{
    if (filled) {
        graphics.beginFill(color);
    } else {
        graphics.lineStyle(strokeWidth, color);
    }
    graphics.drawRect(startX, startY, endX - startX, endY - startY);
}
Looking at the generated code, you’ll see what’s going on behind the scenes with our MXML.
package 
{
//  begin class def
public class FrameworklessChildren
    extends components.MyContainer
{
    //  instance variables
/**
 * @private
 **/
    public var _FrameworklessChildren_Ellipse1 : components.shapes.Ellipse;

/**
 * @private
 **/
    public var _FrameworklessChildren_Rect1 : components.shapes.Rect;
    //  type-import dummies
    //  constructor (non-Flex display object)
    /**
     * @private
     **/
    public function FrameworklessChildren()
    {
        super();
        //    our style settings
        //    properties
        _FrameworklessChildren_Ellipse1_i();
        _FrameworklessChildren_Rect1_i();
        //    events
    }
    //  scripts
    //  end scripts
    //    supporting function definitions for properties, events, styles, effects
private function _FrameworklessChildren_Ellipse1_i() : components.shapes.Ellipse
{
    var temp : components.shapes.Ellipse = new components.shapes.Ellipse();
    _FrameworklessChildren_Ellipse1 = temp;
    temp.color = 65280;
    temp.startX = 20;
    temp.startY = 120;
    temp.endX = 100;
    temp.endY = 130;
    temp.initialized(this, "_FrameworklessChildren_Ellipse1")
    return temp;
}

private function _FrameworklessChildren_Rect1_i() : components.shapes.Rect
{
    var temp : components.shapes.Rect = new components.shapes.Rect();
    _FrameworklessChildren_Rect1 = temp;
    temp.color = 16711680;
    temp.startX = 20;
    temp.startY = 20;
    temp.endX = 100;
    temp.endY = 30;
    temp.initialized(this, "_FrameworklessChildren_Rect1")
    return temp;
}
    //  embed carrier vars
    //  end embed carrier vars
//  end class def
}
//  end package def
}
So basically to create the children it calls the methods in the constructor for the top-level root class. In those methods, the child object gets created, its properties get set, and the initialized method is called. The full code for the above example can be found here. However, as I said before, this doesn’t really support nesting--these objects are essentially just direct children of the root object. So to support this, we need to use default properties. This is actually how the majority of objects will work in Flex 4 (Flex 3 essentially uses something else entirely which is built around UIComponents and UIComponentDescriptors). Anyways, here’s our sample MXML file that we want to support:
<?xml version="1.0" encoding="utf-8"?>
<components:MyContainer 
    xmlns="http://ns.adobe.com/mxml/2009" 
    xmlns:components="components.*" 
    xmlns:shapes="components.shapes.*">
    
    <shapes:Rect color="0xFF0000" startX="20" startY="20" endX="100" endY="30" />
    <shapes:Ellipse color="0x00FF00" startX="20" startY="120" endX="100" endY="130" />
    
    <components:MyContainer x="100" y="100">
        <shapes:Rect color="0xFF0000" startX="20" startY="20" endX="100" endY="30" />
        <shapes:Ellipse color="0x00FF00" startX="20" startY="120" endX="100" endY="130" />
    </components:MyContainer>
</components:MyContainer>
You see I’ve added another container that has children. Another important thing to note is that I changed the compiler namespace to use the new 2009 namespace (so you’ll need the new compiler to take advantage of this). This isn’t actually needed, and I’ll show you what it looks like with just the 2006 namespace later on. The basic idea of default properties is that every MXML object has a "default property." In our case, MyContainer will have a default property of "content." This is basically the property where the children go. Instead of the MXML code above, I could’ve used:
<?xml version="1.0" encoding="utf-8"?>
<components:MyContainer 
    xmlns="http://www.adobe.com/2006/mxml" 
    xmlns:components="components.*" 
    xmlns:shapes="components.shapes.*">
    <components:content>
        <shapes:Rect color="0xFF0000" startX="20" startY="20" endX="100" endY="30" />
        <shapes:Ellipse color="0x00FF00" startX="20" startY="120" endX="100" endY="130" />
        
        <components:MyContainer x="100" y="100">
            <components:content>
                <shapes:Rect color="0xFF0000" startX="20" startY="20" endX="100" endY="30" />
                <shapes:Ellipse color="0x00FF00" startX="20" startY="120" endX="100" endY="130" />
            </components:content>
        </components:MyContainer>
    </components:content>
</components:MyContainer>
The difference here is that when I instantiate MyContainer, I'm explicitly saying "fill the content property with these objects". However, rather than having to explicitly say that the objects should be stuffed into the content property, I can tell MyContainer to have a default property of content. There was a bug in the MXML compiler in how it handled top-level default properties (only top-level ones), and that’s why I had to use the 2009 namespace to use it; however, if you explicitly specify the property, you can use the old compiler and the 2006 namespace. So let’s see how we modified MyContainer to make this happen:
package components
{
    import flash.display.DisplayObjectContainer;
    import flash.display.Sprite;
    
    [DefaultProperty("content")]
    public class MyContainer extends Sprite
    {
        public function MyContainer()
        {
        }
        
        private var _content:*;
        
        public function get content():*
        {
            return _content;
        }
        
        public function set content(value:*):void
        {
            _content = value;
            
            if (_content is Array)
            {
                for (var i:int = 0; i < _content.length; i++)
                {
                    _content[i].initializeMe(this);
                }
            }
            else
            {
                _content.initializeMe(this);
            }
        }
        
        public function initializeMe(parent:DisplayObjectContainer):void
        {
            parent.addChild(this);
        }

    }
}
With the metadata, we defined MyContainer’s default property to "content." Then I just filled in some basic getters/setters for the property. Because content could be a single item or an Array of items, I special cased some logic in there. Basically all I did was loop through all the content and call initializeMe(this) on all the children. That’s just a method I made up, and I probably should create an interface for it. You can see I implemented initializeMe(parent:DisplayObjectContainer) in here as well, and all it does is call addChild. In the case of ArtBoard, it’s pretty similar:
public function initializeMe(parent:DisplayObjectContainer):void
{
    parent.addChild(this);
    renderShape();
}
So to complete the picture of how all this works, let’s take a look at the generated code for our MXML class (the first one which uses default properties):
package 
{
//  begin class def
public class FrameworklessChildren
    extends components.MyContainer
{
    //  instance variables
    //  type-import dummies
    //  constructor (non-Flex display object)
    /**
     * @private
     **/
    public function FrameworklessChildren()
    {
        super();
        //    our style settings
        //    properties
        this.content = [_FrameworklessChildren_Rect1_c(), _FrameworklessChildren_Ellipse1_c(), _FrameworklessChildren_MyContainer2_c()];
        //    events
    }
    //  scripts
    //  end scripts
    //    supporting function definitions for properties, events, styles, effects
private function _FrameworklessChildren_Rect1_c() : components.shapes.Rect
{
    var temp : components.shapes.Rect = new components.shapes.Rect();
    temp.color = 16711680;
    temp.startX = 20;
    temp.startY = 20;
    temp.endX = 100;
    temp.endY = 30;
    return temp;
}

private function _FrameworklessChildren_Ellipse1_c() : components.shapes.Ellipse
{
    var temp : components.shapes.Ellipse = new components.shapes.Ellipse();
    temp.color = 65280;
    temp.startX = 20;
    temp.startY = 120;
    temp.endX = 100;
    temp.endY = 130;
    return temp;
}

private function _FrameworklessChildren_MyContainer2_c() : components.MyContainer
{
    var temp : components.MyContainer = new components.MyContainer();
    temp.x = 100;
    temp.y = 100;
    temp.content = [_FrameworklessChildren_Rect2_c(), _FrameworklessChildren_Ellipse2_c()];
    return temp;
}

private function _FrameworklessChildren_Rect2_c() : components.shapes.Rect
{
    var temp : components.shapes.Rect = new components.shapes.Rect();
    temp.color = 16711680;
    temp.startX = 20;
    temp.startY = 20;
    temp.endX = 100;
    temp.endY = 30;
    return temp;
}

private function _FrameworklessChildren_Ellipse2_c() : components.shapes.Ellipse
{
    var temp : components.shapes.Ellipse = new components.shapes.Ellipse();
    temp.color = 65280;
    temp.startX = 20;
    temp.startY = 120;
    temp.endX = 100;
    temp.endY = 130;
    return temp;
}
    //  embed carrier vars
    //  end embed carrier vars
//  end class def
}
//  end package def
}
You can see it’s basically the same as the one using IMXMLObject; however, rather than calling the initialized() method for us, it just sets the content property. You can download the fill code for this example here. One thing that might be worth checking out is how this class gets generated in the first place. In the compiler, we have a few velocity templates that we use (for style sheets, MXML classes, binding, etc...). The one for the MXML->ActionScript is called ClassDef.vm (a lot of the methods used in that class exist in ClassDefLib.vm). If you’re interested in a lot of this stuff, definitely download the compiler code and take a look at these files. They’re pretty simple to follow and easy to modify if you want to start mucking around with it. So that’s basically it for my short trip into compiler-land. If you’ve got any questions, let me know!