Adobe Catalyst was released at the beginning of June with great fanfare, and with that positive buzz a lot of tutorials have been created that demonstrate how quickly a developer can go from a Catalyst project to a Flash Builder project to a working application. However, there aren't many tutorials on how to go back and forth between Catalyst and Flash Builder during production. The reason is that this type of "roundtrip" functionality doesn't officially exist (yet.) Currently, once a developer modifies the Catalyst generated code to hook buttons to handlers or data lists to services they void their warranty with Catalyst. Conversly, Catalyst can't read fxp files generated by Flash Builder, nor can a developer simply just reimport the project from Catalyst without their custom code getting overwritten.
With these limitations, it seems like Catalyst is limited to prototyping. Subsequently the burden of converting a design to a functioning application and implementing design revisions still predominately rests on the shoulders of the developer.
After working with Catalyst and Flash Builder for the past couple of days, I think I have a process in place that keeps Catalyst involved in the course of a production cycle. With these tips and tricks, you should be able to frequently reimport a Catalyst project and have your Flash Builder project update without having to worry about the aforementioned pitfalls of code getting lost/overwritten.
Keep your Catalyst project flat.
What do I mean by "flat?" When I first started working with Catalyst, I created complex components within complex components. For example, I would create a screen component. Then inside that screen component, I would create a panel component. Inside that panel component I would create a button. This may seem natural and a great way to organize your UI elements, but because you currently cannot declare what the "id" attribute of a component is in Catalyst there isn't a way for you to do something like put a click handler on your button without going into that panel component and adding it in the code with Flash Builder. This, as I mentioned earlier, voids your warranty with Catalyst. So how can you avoid this situation?
Keep all of your components on the root layer of your project.
Avoid having components embedded in components.
If you do this, when you import your Catalyst project into Flash Builder you'll be able pick and choose what components you want to use and where you will want to use them.
Think in terms of skins and not components.
When I say "skin," I do not mean a Spark Skin in the strictest sense. This is because Catalyst can only declare a component as being a Button, TextInput, DataList, or a Custom Component. Hopefully, Adobe will add this feature in a future beta. In the meantime, I tend to create custom components to act as backgrounds and other parts for my Flash Builder components. (I will demonstrate how to execute this later in the post.)
Be sure to give good names to your Catalyst components.
As you build custom components, be sure to go into the library panel for your project in Catalyst and change the names to something more meaningful than "CustomComponent1."
Don't touch the components from Catalyst after importing them into Flash Builder.
By default, Catalyst puts all of your custom components in a "components" directory. If you select "code" from the view options in the upper right, you can see this directory structure. When you create a new Flex project from your Catalyst fxp file or import it into an existing Flex project, you will also see this folder. Do not touch or modify the code of the components in this directory. If you do, do so at your own risk. Just keep in mind that all of your code in this components directory will get overwritten with a later import.
Note: I tried changing this folder structure in code view of Catalyst, but it seems to break the design view of Catalyst.
Implement Catalyst custom components as "skins."
Here's a mxml component I created in Flash Builder called MyPanel.mxml that uses two custom components I created in Catalyst:
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" width="100%" height="100%" xmlns:components="components.*"> <s:layout> <s:BasicLayout/> </s:layout> <components:MyCatalystPanelBG id="myPanelBG"/> <components:MyCatalystHeaderBG id="myPanelHeaderBG" left="5" top="5"/> </s:Group>
By using BasicLayout I can overlap these elements and position them exactly where I want. To add the actual contents of this panel (like buttons, textinputs, lists, etc...) I just add them after the two custom Catalyst components like so:
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" width="100%" height="100%" xmlns:components="components.*"> <s:layout> <s:BasicLayout/> </s:layout> <components:MyCatalystPanelBG id="myPanelBG"/> <components:MyCatalystHeaderBG id="myPanelHeaderBG" left="5" top="5"/> <s:TextInput id="searchTextInput" right="7" text="Enter search criteria here" y="7" /> <s:List id="itemsList" width="100%" height="100%" top="40" left="10" right="10" bottom="10" /> </s:Group>
The beauty with this approach is that if something changes in the Catalyst project, I can import it again without having to worry about losing the business code I have written in Flash Builder.
Use mxmlContent to make skins automatically resize.
You may have noticed in Catalyst you can only declare the height and width of elements as fixed numbers and not percentages. This may be suitable when creating a prototype, but a good Flex app needs to be able to resize as browser height and widths change. The solution? With the new Spark Group component there is a property called "mxmlContent" that is an array of all of the visible children of the component. How do we use this? Let's say the mxml for the component MyCatalystPanelBG looks like this:
<s:Group xmlns:s="library://ns.adobe.com/flex/spark" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt" xmlns:components="components.*"> <s:Rect y="0.5" height="700" radiusX="10" width="238" x="0.5" radiusY="10" d:userLabel="PanelBG"> <s:stroke> <s:SolidColorStroke weight="1" caps="none" joints="miter" miterLimit="4" color="#808080"/> </s:stroke> <s:fill> <s:LinearGradient rotation="90"> <s:GradientEntry color="#ffffff" alpha="1.0" ratio="0"/> <s:GradientEntry color="#d8d8d8" alpha="1.0" ratio="1"/> </s:LinearGradient> </s:fill> </s:Rect> </s:Group>
We can make this background scale with our panel component by doing the following:
Add a "creationComplete" handler in MyPanel.mxml.
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" width="100%" height="100%" xmlns:components="components.*" creationComplete="onCC()"> <fx:Script> <![CDATA[ import spark.primitives.Rect; private function onCC():void{ var r:Rect = myPanelBG.mxmlContent[0] as Rect; r.top = 0; r.left = 0; r.right=0; r.bottom=0; } ]]> </fx:Script>
Also be sure to set the height and width of the "myPanelBG" to 100%.
<components:MyCatalystPanelBG id="myPanelBG" width="100%" height="100%"/>
The mxmlContent property is nice in that you don't have to worry about looping through sub children like you do with "getChildAt(i)" to get to what you want. This line of code basically says "get the first content item which is a rectangle element and set its right, top, bottom, and left properties to 0."
NOTE: This hack is very dependent that the order of elements don't change in your Catalyst components. Whenever updating these components you may need to check to see if the structure has changed in these components. You could do something like have a loop that goes through all of the items in mxmlContent and changes these properties on any Rects.
To bind strings to text items in a Catalyst custom component, use BindUtil.
Let's say you have a RichText component inside your header "skin:"
<s:Group xmlns:s="library://ns.adobe.com/flex/spark" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt"> <s:Rect y="0.5" height="23" radiusX="5" width="737" x="0.5" radiusY="5" d:userLabel="Header BG"> <s:fill> <s:LinearGradient rotation="90"> <s:GradientEntry color="#bf0000" alpha="1.0" ratio="0"/> <s:GradientEntry color="#7f0000" alpha="1.0" ratio="1"/> </s:LinearGradient> </s:fill> <s:stroke> <s:SolidColorStroke color="#7f0000" caps="none" joints="miter" miterLimit="4" weight="1"/> </s:stroke> </s:Rect> <s:RichText y="5" color="#ffffff" x="7" fontSize="17" fontFamily="Arial" fontWeight="bold" d:userLabel="Title"/> </s:Group>
To bind the RichText component to a property in MyPanel.mxml do the following.
Add a title variable to MyPanel.mxml:
public var title:String = "My Panel Title"
In the creation complete handler add the following:
BindingUtils.bindProperty(myPanelHeaderBG.mxmlContent[1], "text", this, "title");
In this scenario, the RichText component is the second item hence the "mxmlContent[1]." That's all you need.
To manage your Catalyst component's state bind it to your Flash Builder component's state.
Let's say you want your "skin" component to have it's state mirror your component that you've written in Flash Builder. To do this all you have to do is this:
<components:MyCatalystPanelBG id="myPanelBG" currentState="{this.currentState}"/>
Note: You need to be sure that you've created all of the necessary states in Catalyst for your skin.
Make your bitmap artwork in Catalyst custom components
This will make managing and swapping out bitmaps much easier as your app's design evolves.
Summary
That should basically do it. A lot of what I have prescribed in here is a bit of a hack until Adobe expands Catalyst's abilities beyond prototyping. However, in the meantime this is still a workable solution to leverage Catalysts abilities in your production phase of your project.
[...] This morning (who said it was a hot subject?) The Morphic Group provided some very good insights in Adobe Flash Builder and Catalyst Tips, Tricks, and Hacks [...]
Your re-sizing thing seems to work pretty well. The drawback is that it scales the whole item — that is, there’s no way to define a 9-slice type resize, for example: to preserve rounded edges.
That seems to be a big issue with Catalyst in its current form. You can produce some nice UI elements easily, but they cannot be scaled readily, and there’s no 9-slice equivalent for vector art.
Hopefully the next Beta addresses these issues.
We had a go at using Catalyst but have basically given up. I think all we will use it for (possibly) is a starting point and each time the design changes either throw our code away or implement the design changes in Flex.
You have a few good work arounds here but relying on the order of the children in the components not changing seems a bit flaky! As simple a change as allowing us to add IDs to components in Catalyst would get round a lot of the issues.
There are other bigger problems though: no scale 9 support, no swc support (everything is a png), no relative positioning – these all stop us using it.
The example that we worked with was a simple login dialogue. The basic dialogue worked fine but when we added an error state with a text field that could take a variable string length we had to throw all the catalyst sizing and positioning code (which is what we wanted it for) away so we could size and position the content according to the size of the text field.
Hey,
We’re having the same issues: no 9slice/resizable component support makes this tool mostly useless… I’ve posted about this in “catalyst ideas”, please vote &/or comment:
http://ideas.adobe.com/ct/ct_a_view_idea.bix?c=DA4859AD-8934-4F93-983A-4219E2DD9275&idea_id=7C340451-271E-4C0D-AF5E-E32CFAB244EC
Thanks