tag:blogger.com,1999:blog-38734988346548118722024-02-07T19:50:17.490-08:00Frish's BlogRyan's blog on Adobe Flex, web technology, and other miscellaneaRyanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.comBlogger19125tag:blogger.com,1999:blog-3873498834654811872.post-58025015752741393812011-09-07T12:45:00.000-07:002011-09-07T12:46:02.132-07:00Adobe MAX 2010: Performance Tips and Tricks for Flex and Flash Development<div><b>This blog has moved to http://www.frishy.com. Please update to the new feed.</b></div>
Last year at Adobe MAX, I presented a talk on performance with Flash and Flex. I covered a lot of different tips and information that is useful for Flash and Flex developers. Check out the video on <a href="http://tv.adobe.com/watch/max-2010-develop/performance-tips-and-tricks-for-flex-and-flash-development-/" title="AdobeTV">AdobeTV</a>.
<div><b>This blog has moved to http://www.frishy.com. Please update to the new feed.</b></div>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com0tag:blogger.com,1999:blog-3873498834654811872.post-53168067155024894342011-08-20T06:30:00.001-07:002011-08-23T14:21:50.424-07:00Intro to Spark Containers<div><b>This blog has moved to http://www.frishy.com. Please update to the new feed.</b></div><div>
</div>Here's a talk I did at work around Spark Containers. Some of the topics covered are:<div>
</div><div><ul><li>Types of Spark Containers</li><li>Types of IVisualElements</li><li>Group Basics</li><li>The multi-faces of DOM</li><li>Lesser known container features</li><li>Internals of Group and SkinnableContainer</li><li>Performance considerations</li></ul><div>
</div></div>
<div><a href="http://www.frishy.com/blogAssets/SparkContainersPreso/Flex%204%20Containers%20Presentation.pptx">PowerPoint Presentation File</a> | <a href="http://www.frishy.com/blogAssets/SparkContainersPreso/Flex%204%20Containers%20Presentation.pdf">PDF Presentation File</a></div>
<div><br/><br/></div>
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dyD-8Ujn89oEjh_qp27oojDyv2c00yN3SYXo5BBXMGekRer-3R2ff-BPsv1DwjW1NqytuRO_1Ny_-IKAl1S8Q' class='b-hbp-video b-uploaded' frameborder='0'></iframe>
<div><b>This blog has moved to http://www.frishy.com. Please update to the new feed.</b></div>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com0tag:blogger.com,1999:blog-3873498834654811872.post-21679549791860641472011-08-20T06:28:00.000-07:002011-08-20T06:29:44.715-07:00I'm movingThis blog is moving to <a href="http://www.frishy.com">http://www.frishy.com</a> . I'll continue double-posting here for a while, but please go ahead and update your RSS readers.Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com0tag:blogger.com,1999:blog-3873498834654811872.post-48597097365619109892011-06-18T11:55:00.000-07:002011-06-18T13:04:34.043-07:00Binding Dependencies<div>A common problem I've noticed, even on the SDK team, was when you have one property dependent on another property (or a set of other properties), and you need that property to be Bindable. For example, in <code class="js">spark.components.VideoPlayer</code>, <code class="js">volume</code> is dependent on the <code>videoDisplay.volume</code> property. Usually to make such a property bindable, you add an event listener to know when your dependent property changes. In this case, if you check out <code class="js">VideoPlayer.partAdded()</code>, you can see code like that. However, when I started using the presentation model pattern, this issue seemed to keep coming up quite frequently.</div>
<div>Let's take a simple example where someone is placing an order. If that person has a billing address and a shipping address in California, we need to show them some extra questions because of extra regulations in California.</div>
<div>Our made up model looks like:</div>
<div><pre><code class="js">
public class Order
{
[Bindable]
public var billingState:String;
[Bindable]
public var shippingState:String;
}
</code></pre></div>
<div>And in the View, we want to do something like:</div>
<div><pre><code class="xml">
<s:Form>
<s:FormItem label="Billing State">
<s:DropDownList selectedItem="@{ presentationModel.order.billingState }">
<s:ArrayList>
<fx:String>AL</fx:String>
<fx:String>AS</fx:String>
<fx:String>AZ</fx:String>
<fx:String>AR</fx:String>
<fx:String>CA</fx:String>
<fx:String>CO</fx:String>
<fx:String>CT</fx:String>
<fx:String>DE</fx:String>
</s:ArrayList>
</s:DropDownList>
</s:FormItem>
<s:FormItem label="Shipping State">
<s:DropDownList selectedItem="@{ presentationModel.order.shippingState }">
<s:ArrayList>
<fx:String>AL</fx:String>
<fx:String>AS</fx:String>
<fx:String>AZ</fx:String>
<fx:String>AR</fx:String>
<fx:String>CA</fx:String>
<fx:String>CO</fx:String>
<fx:String>CT</fx:String>
<fx:String>DE</fx:String>
</s:ArrayList>
</s:DropDownList>
</s:FormItem>
<s:FormItem label="California Question1"
includeInLayout="{ presentationModel.order.shippingState == 'CA' &amp;&amp; presentationModel.order.billingState == 'CA' }"
visible="{ presentationModel.order.shippingState == 'CA' &amp;&amp; presentationModel.order.billingState == 'CA' }">
<s:TextInput />
</s:FormItem>
<s:FormItem label="California Question2"
includeInLayout="{ presentationModel.order.shippingState == 'CA' &amp;&amp; presentationModel.order.billingState == 'CA' }"
visible="{ presentationModel.order.shippingState == 'CA' &amp;&amp; presentationModel.order.billingState == 'CA' }">
<s:TextInput />
</s:FormItem>
</s:Form>
</code></pre></div>
<div>Now the same code: <code class="js">presentationModel.order.shippingState == 'CA' &amp;&amp; presentationModel.order.billingState == 'CA'</code> is repeated 4 times, the ampersands have to be escaped, and overall it's just quite ugly. Now, one of the neat tricks I've learned is that you can spruce it up by doing something like:</div>
<div><pre><code class="xml">
<fx:Declarations>
<fx:Boolean id="showCaliforniaQuestions">
{presentationModel.order.shippingState == "CA" &amp;&amp;
presentationModel.order.billingState == "CA"}
</fx:Boolean>
</fx:Declarations>
...
<s:FormItem label="California Question1"
includeInLayout="{ showCaliforniaQuestions }"
visible="{ showCaliforniaQuestions }">
<s:TextInput />
</s:FormItem>
<s:FormItem label="California Question2"
includeInLayout="{ showCaliforniaQuestions }"
visible="{ showCaliforniaQuestions }">
<s:TextInput />
</s:FormItem>
</code></pre></div>
<div>That definitely works and is much cleaner than before; however, that means we have some business logic in our View that is extremely hard to test. That logic should really be in the presentation model. If we try to move it into the Presentation Model, we'll end up with something that looks like: </div>
<div><pre><code class="xml">
<s:FormItem label="California Question1"
includeInLayout="{ presentationModel.showCaliforniaQuestions }"
visible="{ presentationModel.showCaliforniaQuestions }">
<s:TextInput />
</s:FormItem>
<s:FormItem label="California Question2"
includeInLayout="{ presentationModel.showCaliforniaQuestions }"
visible="{ presentationModel.showCaliforniaQuestions }">
<s:TextInput />
</s:FormItem>
</code></pre></div>
<div><pre><code class="js">
public class MyPM
{
public function MyPM()
{
super();
}
[Bindable]
public var order:Order = new Order();
public function get showCaliforniaQuestions():Boolean
{
return order.shippingState == "CA" && order.billingState == "CA";
}
}
</code></pre></div>
<div>However, doing that doesn't work. The reason is because <code class="js">showCaliforniaQuestions</code> isn't Bindable. Now, to make it Bindable, we need to add Bindable metadata on top, add event listeners (or use a BindingWatcher) to let us know when the shipping state or the billing state changes, and dispatch a new binding event when we get notified that the shipping or billing state changes. This turns out to be quite a lot of extra, ugly code, which means most people just end up keeping this logic in the View because practically it's just too much work and too ugly to move it to the Presentation Model. This is all fine, but this kept issue kept cropping up in practice, so I finally sat down to come up with a more palatable option.</div>
<div>One of the very cool things about Parsley is that you can add your own custom Metadata, and Parsley will help process it for you. Even though I'm really new to Parsley, it turns out that this is relatively easy to do. So I decided to go ahead and add some custom metadata to help deal with this. What's really neat is that I added a Parsley processor for Bindable metadata. I added a new property on that metadata, which allows you to define binding dependencies--basically Bindings that are dependent on other properties. In our particular example, here's what it looks like:</div>
<div><pre><code class="js">
[Bindable(event="showCaliforniaQuestionsChanged",dependentProperty="order.shippingState")]
[Bindable(event="showCaliforniaQuestionsChanged",dependentProperty="order.billingState")]
public function get showCaliforniaQuestions():Boolean
{
return order.shippingState == "CA" && order.billingState == "CA";
}
</code></pre></div>
<div>All we did was declare the <code class="js">showCaliforniaQuestions</code> as Bindable, where that binding is dependent on the <code class="js">order.shippingState</code> and <code class="js">order.billingState</code> properties. The Parsley metadata processor will step in and handle everything else. I personally like this solution a lot as it piggy-backs off of the Flex SDK framework's Bindable metadata and just extends it for our purpose. It's easy to use and pushes our logic into Presentation Model, which makes our code cleaner and more importantly, testable.</div>
<div>The main downside to this approach is that we're exposed to spelling mistakes or later refactorings since the dependentProperty in the Bindable metadata is not checked by the compiler. Ideally, this would be something we could add to the ActionScript compiler so that it could inspect the getter and pick out all the bindable variables. However, we don't have that, and I find this pattern really convinient for me. The code for all of this can be found in the attached <a href="https://sites.google.com/site/frishysblogupload/Home/ParsleyBindingDependency.zip">Flash Builder project</a>--feel free to use it.</div>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com0tag:blogger.com,1999:blog-3873498834654811872.post-62463242099741384122011-06-09T13:49:00.000-07:002011-06-10T15:23:58.421-07:00Why Presentation Models can be confusing<div><span class="Apple-style-span">When I decided to leave Adobe and the Flex SDK team after almost 4 years, I left because I wanted to see what issues people were encountering with the SDK and to learn what some of the challenges are with application development. Well, one of the first things you're bound to encounter as an application developer working on a large application is some way to separate the logic of the View from the actual look of the View (as well as the actual business logic, but I won't talk about that here). There are lots of ways to achieve this separation, and I'm not going to debate which way is best to it However, I'm going to take a look at the Presentation Model pattern and go through some of the questions I had when I was trying to understand this pattern. Luckily for me, I have a lot of great co-workers, like Miguel Sanchez and Dan Johnson who helped me understand it.</span></div><div><span class="Apple-style-span">
</span></div><div><span class="Apple-style-span" ><b>Introduction:</b></span></div><div><span class="Apple-style-span">
</span></div><div><span class="Apple-style-span">The Presentation Model is a standard pattern where logic to control the state and behavior of the view is extracted from the view itself. Simply, the Presentation Model (PM) controls what the View displays--the View controls how to display that information. The PM coordinates with the domain layer to retrieve information and perform actions.</span></div><div><span class="Apple-style-span">
</span></div><div><span class="Apple-style-span" ><b>Goals:</b></span></div><div><span class="Apple-style-span">
</span></div><div><span class="Apple-style-span">The purpose of the Presentation Model pattern is to:</span></div><div><ul><li><span class="Apple-style-span">Separate out the state and behavior of the view from the presentation of the View itself</span></li><li><span class="Apple-style-span">Provide a GUI agnostic, testable interface</span></li><li><span class="Apple-style-span">Provide a consisten, performant pattern that developers can easily follow</span></li></ul><div><span class="Apple-style-span" ><b>Definitions:</b></span></div></div><div><span class="Apple-style-span">
</span></div><div><span class="Apple-style-span"><b>View</b> - A class defining the GUI for a particular part of the application. The View may be composed of other Views or other UI controls</span></div><div><span class="Apple-style-span">
</span></div><div><span class="Apple-style-span"><b>Presentation Model (PM)</b> - An abstraction of a particular View. It controls the state and logic for a particular View; however, it does not control how the View presents this information.</span></div><div><span class="Apple-style-span">
</span></div><div><span class="Apple-style-span"><b>Domain Layer</b> - A generic term for the business logic for a particular application. This may include domain models, commands, service layers, controllers, etc...</span></div><div><span class="Apple-style-span">
</span></div><div><span class="Apple-style-span" ><b>Overview:</b></span></div><div><span class="Apple-style-span"><b>
</b></span></div><div><span class="Apple-style-span"><b><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZoGKyGkqzn5SmIoP8GQRtsxbAMdR4P9cJHg5gFCOfe9Z7-PwWM8pJy8VyWtF1qpLz_Pt5SP7HBVfHYkMJPrEIvI560gax-LWWPRjY6_NyAa2eyicOP_6Zj_3KKYE4XfXHU_a7r5ziDos/s1600/PresentationModelPattern.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZoGKyGkqzn5SmIoP8GQRtsxbAMdR4P9cJHg5gFCOfe9Z7-PwWM8pJy8VyWtF1qpLz_Pt5SP7HBVfHYkMJPrEIvI560gax-LWWPRjY6_NyAa2eyicOP_6Zj_3KKYE4XfXHU_a7r5ziDos/s320/PresentationModelPattern.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5616347991868712834" /></a>
</b></span></div><div><span class="Apple-style-span"><b>
</b></span></div><div><span class="Apple-style-span" ><b>View Responsibilities:</b></span></div><div><ul><li><span class="Apple-style-span">The View should contain very little logic--only GUI specific, layout implementation should be contained in the View</span></li><li><span class="Apple-style-span">The View should use databinding to grab information from the Presentatio Model</span></li><li><span class="Apple-style-span">The View should call methods directly into the PM (see FAQ for description of right level of abstraction)</span></li><li><span class="Apple-style-span">The View should attach Flash event listeners on the PM to receive any event information (if using Parsley, the View should not communicate to anyone using Parsley messaging as configuring a display object in Parsley is a huge performance hit)</span></li></ul><div><div><span class="Apple-style-span" ><b>Presentation Model Responsibilities:</b></span></div><div><ul style="font-size: medium; "><li style="font-size: 16px; "><span class="Apple-style-span">The PM should hold the logic, state, and data of the user interface. It is essentially an abstract, non-GUI dependent abstraction of a View</span></li><li style="font-size: 16px; "><span class="Apple-style-span">The PM controls what the View displays (how to display it is up to the View)</span></li><li style="font-size: 16px; "><span class="Apple-style-span">The View calls into the PM via public methods. The PM then takes those actions and talks to the Domain Layer.</span></li><li>For the PM to talk to the View, it should use properties with data-binding or Flash events.</li></ul><div><span class="Apple-style-span" ><b>FAQ (aka-Questions I had)</b></span></div></div></div></div><div><span class="Apple-style-span"><b>
</b></span></div><div><span class="Apple-style-span"><b>If the Presentation Model is tied 1:1 to a View, why create another class, when the View can contain that logic directly? </b></span><span class="Apple-style-span" style="font-size: medium; ">The main purpose of the PM is to separate out what the View should display from how it should be displayed. The PM controls the state, behavior, and any business logic for the View. The View controls how to display the appropriate information. </span><span class="Apple-style-span" style="font-size: medium; "><b> </b>The point of the PM is <b>not</b> to be reusable in other Views. In order to foster code re-usability, put similar code into the Domain Layer or into the other PMs that can be used by your PM. As Martin Fowler says, "Presentation Model may interact with several domain objects, but Presentation Model is not a GUI friendly facade to a specific domain object. Instead, it is easier to consider Presentation Model as an abstract of the view that is not dependent on a specific GUI framework." </span></div><div>
</div><div><b>What is the right level of abstraction for the View and the PM to talk to each other?</b> Here are some examples of how the View may talk to the PM:</div><div><ol><li><s:Button id="submitButton" click="{ myPM.submitButton_clickHandler }" /></li><li><s:Button id="submitButton" click="myPM.onSubmitButton(busIdTI.text)" /></li><li><s:Button id="submitButton" click="myPM.setBusId(parseInt(busIdTI.text))" /></li><li><s:Button id="submitButton" click="myPM.unsubscribe(); myPM.setCurrentBus(new Bus(parseInt(busIdTI.text))); myPM.subscribe();" /></li></ol><div>There is no right or wrong answer here, but in general, the approach should probably be #2, #3, or somewhere in between. Try to stay away from #4 where the View does too much work. Try to stay away from #1 where the PM now knows about the GUI concepts (a MouseEvent in this case).</div></div><div>
</div><div><b>If your View is displaying data in a grid, where should the column definitions for that grid go? </b>The Presentation Model defines what the View display; however, the goal should be to do that in a GUI agnostic approach. Ideally, the PM should define what columns need to be displayed as Strings or Enums, but it wouldn't actually define DataGridColumn instances. In a practical world, this ideal separation doesn't always make sense and can introduce a lot of unnecessary "translation code" (code whose sole purpose is to take the abstract output from the PM and concert it into a GUI specific implementation); however, in almost all cases, that goal should be striven for as it makes the Presentation Model more testable.</div><div>
</div><div><b>What is the difference between a "View" and an ordinary UI control? </b>A View is a UI control, but it is a special UI control that represents an abstract interface for your particular application. A View may contain other Views and other UI controls. In general, not all custom components or views need to have a Presentation Model, but if the view has a reasonable amount of state and behavior, then the code should be split up into a View and a separate, testable Presentation Model. In general, it's up to the developer to make this judgement call, but a lot can be said for the consistency of always having a Presentation Model, even if it doesn't do much.</div><div>
</div><div><b>Can a Presentation Model contain other Presentation Models?</b> In general it can. There are two basic models for this (atleast that I know of--see <a href="http://blogs.adobe.com/tomsugden/2009/08/applying_the_presentation_mode.html">http://blogs.adobe.com/tomsugden/2009/08/applying_the_presentation_mode.html</a>). In the Componentized Presentation Model, the View can contain other sub-Views. Those sub-Views have references to their own PM, but the main View's PM does not have direct references to the sub-Views' PM. In the Hierarchal Presentation Model pattern, the main PM contains references to the other PMs directly.</div><div>
</div><div><b>How does the Presentation Model get attached to the View? </b>In Parsley, the top-level view creates a Parsley context. This parsley context instantiates the Presentation Model needed for that View. The View has a FastInject tag to grab the Presentation Model. When using the Componentized Presentation Model (Views contain other sub-Views that need their own Presentation Model), the Parsley context also defines the sub-View PMs (either as a single instance if only one sub-View will be created or as a DynamicObject if multiple sub-Views are needed). To communicate with other Presentation Models, use Parsley messages. In the Hierarchical Presentation Model, the main Presentation Model would instantiate the sub-View presentation models directly and can be used to communicate with the sub-Views directly.</div><div>
</div><div><span class="Apple-style-span" ><b>Conclusion:</b></span></div><div><span class="Apple-style-span" >
</span></div><div><span class="Apple-style-span" >It took me a while to understand why the Presentation Model pattern would be useful. In my first attempts at it, I kept making the "Presentation Model" more of a wrapper for a Domain Model object--something that could be re-used in multiple Views. Though having an object like that is useful--that is not the Presentation Model. The Presentation Model is there to help </span><span class="Apple-style-span" style="font-size: medium; ">separate out the look of the View from the state and behavior of the View.</span></div><div><span class="Apple-style-span" style="font-size: medium; ">
</span></div><div><span class="Apple-style-span" style="font-size: medium; ">In a lot of ways, this separation reminds me a lot of a SkinnableComponent vs. a Skin. However, there's a more explicit contract in Flex 4 around SkinnableComponents and Skins; there are a set number of supported and toolable ways that the SkinnableComponent and the Skin can talk to each other. Having a tight contract like that and tooling support can be really useful, and it might be useful for the Presentation Model pattern as well. However, more on that and other issues I've encountered at a later date. I'm still relatively new to Presentations Models, so comments are much appreciated.</span></div><div>
</div><div><span class="Apple-style-span" ><b>References:</b></span></div><div><ul><li><span class="Apple-style-span"><a href="http://martinfowler.com/eaaDev/PresentationModel.html">http://martinfowler.com/eaaDev/PresentationModel.html</a></span></li><li><a href="http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html">http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html</a></li><li><a href="http://blogs.msdn.com/b/erwinvandervalk/archive/2009/08/14/the-difference-between-model-view-viewmodel-and-other-separated-presentation-patterns.aspx">http://blogs.msdn.com/b/erwinvandervalk/archive/2009/08/14/the-difference-between-model-view-viewmodel-and-other-separated-presentation-patterns.aspx</a></li><li><a href="http://artinflex.blogspot.com/2010/11/presentation-model-implemented-in.html">http://artinflex.blogspot.com/2010/11/presentation-model-implemented-in.html</a></li><li><a href="http://www.spicefactory.org/parsley/docs/2.2/manual/mvc.php">http://www.spicefactory.org/parsley/docs/2.2/manual/mvc.php</a></li><li><a href="http://blogs.adobe.com/tomsugden/2009/08/applying_the_presentation_mode.html">http://blogs.adobe.com/tomsugden/2009/08/applying_the_presentation_mode.html</a></li></ul></div>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com5tag:blogger.com,1999:blog-3873498834654811872.post-26606561761796473542010-02-22T22:09:00.000-08:002010-02-22T23:00:33.974-08:00When to use what Spark Container<style type="text/css">
<!--
.code {
font-family: Courier New, Courier, monospace;
font-size: 11pt;
}
-->
</style>
<p>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 <a href="http://forums.adobe.com/message/2603068">forum post</a>.</p>
<div>
</div><div><b>Question: <span class="Apple-style-span" style="font-weight: normal; ">We know that Flex 4 has all this new container as <strong>Group, VGroup, HGroup</strong>, etc...as well as the old Halo componentas as the <strong>Canvas, Panel, VBox, HBox</strong>, etc...</span></b></div><p>This gets me really confused on which container i should use with my project.</p><p>For example, what's the difference between:</p>
<p class="code"><s:Group><br />
<s:layout><br />
<s:HorizontalLayout /><br />
</s:layout><br />
</s:Group></p>
<p>...and...</p>
<p class="code"><s:HGroup><br />
</s:HGroup></p>
<p>...or even...</p>
<p class="code"><mx:HBox><br />
</mx:HBox></p>
<p>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.</p><p>One other thing is if i need to do a container with a background color. What should i use? </p><p class="code"><mx:Canvas backgroundcolor="{color}"><br />
<MyComponents /> <br />
</mx:Canvas></p><p>...or...</p>
<p class="code"><s:Group><br />
<s:Rect><br />
<s:fill><br />
<s:SolidColor color="{color}" /><br />
</s:fill><br />
</s:Rect><br />
<MyComponents /><br />
</s:Group></p><p>Can anyone tell me which one is the best pratice now for Flex 4?</p>
<p><b>Answer:</b></p><p><span class="Apple-style-span" style="font-weight: normal;">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:</span></p>
<p class="code"><s:Group><br />
<s:layout><br />
<s:VerticalLayout horizontAlign=".." /><br />
</s:layout><br />
...<br />
</s:Group></p>
<p><span class="Apple-style-span" style="font-weight: normal;">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.</span></p><p><span class="Apple-style-span" style="font-weight: normal;"><b>In summary:</b> </span></p><p><span class="Apple-style-span" style="font-weight: normal;">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).</span></p><p style="padding: 0px; min-height: 8pt"><span class="Apple-style-span" style="font-weight: normal;">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.</span></p>
<p>As per the backgroundColor part of your question, you could composite it together like you did in the Group:</p><p style="padding: 0px; min-height: 8pt" class="code"><s:Group><br />
<s:Rect><br />
<s:fill><br />
<s:SolidColor color="{color}" /><br />
</s:fill><br />
</s:Rect><br />
<MyComponents /><br />
</s:Group><br />
</p>
<p style="padding: 0px; min-height: 8pt;">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:</p>
<p class="code" style="padding: 0px; min-height: 8pt"><s:SkinnableContainer backgroundcolor="red"><br />
<strong><em style="color: rgb(51, 51, 51);"> </em></strong>...<br />
</s:SkinnableContainer> </p><p>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).</p><p>Hope this helps answer a lot of questions.</p><p>-Ryan</p>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com4tag:blogger.com,1999:blog-3873498834654811872.post-22825007924229418052009-03-27T23:56:00.000-07:002009-03-28T00:01:05.349-07:00BugQuashThere's an awesome event going on tomorrow--<a href="http://bugquash.com/">BugQuash</a>. 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.Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com0tag:blogger.com,1999:blog-3873498834654811872.post-39025370857708998362008-12-14T23:14:00.000-08:002008-12-14T23:19:54.825-08:00Flex Videos on your iPodOur 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: <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=299639895">http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewPodcast?id=299639895</a>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com0tag:blogger.com,1999:blog-3873498834654811872.post-76886677651577293132008-11-19T01:54:00.000-08:002010-01-31T19:01:12.736-08:00MAX 2008: Item Renderer TalkMAX 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 <a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/RatingExample4.swf?sa=499723500">last example</a> we didn't get to show.
Anyways, take a look at the slides & examples, and let me know if you've got any questions.
<a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/Custom%20ItemRenderers.pdf%20">Slides (PDF)</a> | <a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/Custom%20ItemRenderers.pptx%20">Slides (PPTX)</a>
<a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/Max360ItemRendererTalkFBCode.zip%20">Examples</a>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com1tag:blogger.com,1999:blog-3873498834654811872.post-50587005941426690812008-11-17T01:48:00.000-08:002008-11-17T01:57:49.208-08:00MAX 2008I'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.Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com1tag:blogger.com,1999:blog-3873498834654811872.post-56173773774574510522008-10-14T22:32:00.000-07:002008-11-12T18:32:13.282-08:00SWCs, Modules, and RSLsSo 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: <a href="http://www.adobe.com/devnet/flex/videotraining/">http://www.adobe.com/devnet/flex/videotraining/</a>
The source code for the demos can be found here:
<ul><li><a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/SWC%20Flex%20Builder%20Projects.zip">SWC Example Source Code</a></li><li><a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/RSL%20Flex%20Builder%20Projects.zip">RSL Example Source Code</a></li><li><a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/Module%20Flex%20Builder%20Projects.zip">Modules Example Source Code</a>
</li></ul>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com3tag:blogger.com,1999:blog-3873498834654811872.post-56450315439130676032008-07-21T21:08:00.000-07:002008-07-21T21:16:16.182-07:00Creating a Frameworkless MXML File Part 2 (Having Children)<div style="font-size: 10pt; font-family:Arial">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:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC">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 <code>initialized()</code>
* 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 <code>document</code> to refer
* to this object.
* If the object is a deep property on <code>document</code>,
* <code>id</code> is null.
*/
function initialized(document:Object, id:String):void;
}
}</pre>
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, <a href="http://graphics-geek.blogspot.com/">Chet Haase</a>, and modified to make it as simple as possible and fit the example.
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC"><?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></pre>
Rather than post all the code here, I’ll just post some snippets (full code is in two flex builder projects: <a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/FrameworklessChildrenIMXMLObject.zip">IMXMLObject example</a> and the <a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/FrameworklessChildrenDefaultProperty.zip">Default property example</a>). 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:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC">public function initialized(document:Object, id:String):void
{
document.addChild(this);
renderShape();
}</pre>
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:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC">/**
* 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);
}</pre>
Looking at the generated code, you’ll see what’s going on behind the scenes with our MXML.
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC">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
}</pre>
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 <a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/FrameworklessChildrenIMXMLObject.zip">here</a>.
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:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC"><?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></pre>
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:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC"><?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></pre>
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:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC">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);
}
}
}</pre>
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:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC">public function initializeMe(parent:DisplayObjectContainer):void
{
parent.addChild(this);
renderShape();
}</pre>
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):
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal; background-color: 0xCCCCCC">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
}</pre>
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 <a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/FrameworklessChildrenDefaultProperty.zip">here</a>.
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!</div>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com5tag:blogger.com,1999:blog-3873498834654811872.post-65822524057226336992008-07-10T23:01:00.000-07:002009-02-09T16:01:58.727-08:00Creating a Frameworkless MXML File<div style="font-size: 10pt; font-family: Arial;">Me saying it’s been a while since I’ve blogged here is an understatement. I’ve been heads down doing work on Flex 4, atleast that’s my excuse, and I’m sticking to it.
Along the way of deep-diving into the framework, I’ve learned how much of the nitty-gritty details actually work. Unfortunately, not all of it would be really valuable to you guys, but as I think of useful lessons I’ve learned, I’ll blog about them here.
Anyways, on to the real post: using the Flex Builder IDE and the MXML compiler for generating framework-less code. By framework-less code, I mean no mx.core.*, no SystemManager, no Application, no UIComponents, etc… So why would you want to do this? Let’s say you want to create something really light-weight and don’t want to carry around some of the framework baggage, but you still want to use FlexBuilder IDE because it’s made for programmers, unlike the Flash Authoring IDE. Or, you want to create an ActionScript only project, but love the MXML syntax because it’s a lot easier for non-progammers to take a look at the MXML code and figure out what’s going on. Some awesome features won’t be available if you don’t use the framework (for instance, binding), but this technique might prove useful to some.
At first, I didn’t know you could actually do this, but it’s quite easy. If you just have one file that is your main ActionScript file, in fact you don’t have to do anything. So let’s say I wanted to create a clock (and not a sweet analog clock, but just a digital one because I’m lame).
Pop open FlexBuilder and create a simple project. Create a simple class called SimpleDigitalClock.as. You’ve got to make sure it extends Sprite because in Flash, the root object must always be a Sprite. My primitive understanding of the reason is that every SWF must have a root object that needs to be a Sprite. This object will be instantiated automatically by the Flash player.
Our clock is going to be super-simple (I’m super-lazy). Here’s my version, but feel free to do something fancier with yours:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal;">package
{
import flash.display.Sprite;
import flash.events.TimerEvent;
import flash.text.TextField;
import flash.utils.Timer;
public class SimpleDigitalClock extends Sprite
{
public function SimpleDigitalClock()
{
super();
time = new TextField();
addChild(time);
updateTime();
timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, updateTime);
timer.start();
}
private var time:TextField;
private var timer:Timer;
private function updateTime(event:TimerEvent = null):void
{
time.text = new Date().toString();
}
}
}
</pre>
As mentioned before I extend Sprite. In the constructor I create a new TextField and add it as a child. Then I call updateTime, which grabs the current time and puts it into our TextField. Lastly, I just create a Timer which runs every second to update the time so it stays current.
So my FlexBuilder project looks like this (just two files):
Now for the cool part. Let’s say I wanted to make this my Application. Well it’s actually really easy, just change your main MXML file to look like this:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal;"><?xml version="1.0" encoding="utf-8"?>
<local:SimpleDigialClock xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*">
</local:SimpleDigitalClock>
</pre>
We can run this file, and we get this beauty:
Some points to note:
<ul>
<li>We kept the mx namespace because the compiler needs this info, even though no components are instantiated from the mx namespace (hopefully I’ll talk about how this is done in another blog).</li>
<li>xmlns:local=”*” creates a new namespace, called local, which maps to actionscript files in my current directory.</li>
<li>All we need to do is tell the MXML compiler to instantiate our Sprite object, SimpleDigitalClock, as the root object for this SWF.</li></ul>
So that’s pretty damned simple. One of the things I often do is to delve into the compiled code. To do this, add “-keep” or “-keep-actionscript-generated” to the additional compiler arguments:
A new folder will popup called “generated.” This will be especially small because we’re using framework-less code. In fact, it’s only 2 files: FrameworkLessClock-generated.as and FrameworkLessClock-interface.as. Only the first one’s really interesting:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal;">/**
* Generated by mxmlc 4.0
*
* Package:
* Class: FrameworkLessClock
* Source: C:\Documents and Settings\rfrishbe\My Documents\Gumbo-FP10-MyBranch\FrameworkLessClock\src\FrameworkLessClock.mxml
* Template: flex2/compiler/mxml/gen/ClassDef.vm
* Time: 2008.07.10 18:37:50 PDT
*/
package
{
import SimpleDigitalClock;
import flash.accessibility.*;
import flash.debugger.*;
import flash.display.*;
import flash.errors.*;
import flash.events.*;
import flash.external.*;
import flash.filters.*;
import flash.geom.*;
import flash.media.*;
import flash.net.*;
import flash.printing.*;
import flash.profiler.*;
import flash.system.*;
import flash.text.*;
import flash.ui.*;
import flash.utils.*;
import flash.xml.*;
import mx.binding.*;
import mx.core.ClassFactory;
import mx.core.DeferredInstanceFromClass;
import mx.core.DeferredInstanceFromFunction;
import mx.core.IDeferredInstance;
import mx.core.IFactory;
import mx.core.IPropertyChangeNotifier;
import mx.core.mx_internal;
import mx.styles.*;
// begin class def
public class FrameworkLessClock
extends SimpleDigitalClock
{
// instance variables
// type-import dummies
// constructor (non-Flex display object)
/**
* @private
**/
public function FrameworkLessClock()
{
super();
// our style settings
// properties
// events
}
// scripts
// end scripts
// supporting function definitions for properties, events, styles, effects
// embed carrier vars
// end embed carrier vars
// end class def
}
// end package def
}
</pre>
So you see not much happens. There’s a lot of comments in there for some framework stuff, but not much happens in our case because we don’t need that stuff. Let’s say you add event-listeners or properties to your SimpleDigitalClock. Those will get genned in here. So for a simple example, let’s create a property called timeZoneOffset, which will be the timezone offset (in minutes) that we want to change our clock to. I won’t go into the details of the math, but I think it works out… Also, let’s create a clockUpdated event, which dispatches anytime we change the text on the clock. The important pieces are highlighted in red:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal;">package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.text.TextField;
import flash.utils.Timer;
<b style="">[Event("clockUpdated")]</b>
public class SimpleDigitalClock extends Sprite
{
public function SimpleDigitalClock()
{
super();
time = new TextField();
addChild(time);
timer = new Timer(1000);
timer.addEventListener(TimerEvent.TIMER, updateTime);
timer.start();
}
private var time:TextField;
private var timer:Timer;
<b style="">public var timeZoneOffset:int = new Date().getTimezoneOffset();</b>
private function updateTime(event:TimerEvent = null):void
{
var date:Date = new Date();
// converts the Date to UTC by adding or subtracting the time zone offset
var currentOffsetMilliseconds:Number = date.getTimezoneOffset() * 60 * 1000;
var newOffsetMilliseconds:Number = timeZoneOffset * 60 * 1000;
date.setTime(date.getTime() + currentOffsetMilliseconds);
date.setTime(date.getTime() - newOffsetMilliseconds);
time.text = date.toString();
<b style="">dispatchEvent(new Event("clockUpdated"));</b>
}
}
}
</pre>
The two things that really matter here are adding the public property and the event metadata. We also dispatch the event in updateTime, otherwise it’d never get fired.
So let’s change these variables in MXML:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal;"><?xml version="1.0" encoding="utf-8"?>
<local:SimpleDigitalClock xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*"
timeZoneOffset="480" clockUpdated="trace('blah')">
</local:SimpleDigitalClock>
</pre>
Now, let’s take a look at our genned code from this example:
<pre style="color: rgb(0, 0, 153); font-size: 9pt; font-family: courier new; line-height: normal;"> // constructor (non-Flex display object)
/**
* @private
**/
public function FrameworkLessClock()
{
super();
// our style settings
// properties
this.timeZoneOffset = 480;
// events
this.addEventListener("clockUpdated", ___FrameworkLessClock_SimpleDigitalClock1_clockUpdated);
}
// scripts
// end scripts
// supporting function definitions for properties, events, styles, effects
/**
* @private
**/
public function ___FrameworkLessClock_SimpleDigitalClock1_clockUpdated(event:flash.events.Event):void
{
trace('blah')
}
</pre>
You can see from the genned code it added the property we set as well as the event and a function for the event handler.
So that’s it for now. I plan on blogging some more on this stuff, covering how to add children in MXML without the framework as well as some other cool topics.
Post a comment if you have any questions or future blog posts that might be useful.</div>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com2tag:blogger.com,1999:blog-3873498834654811872.post-71467112689902349792008-02-25T00:34:00.000-08:002008-02-25T00:37:01.354-08:00As usual, i really need to be blogging here more :-)
At any rate, if you haven't heard, AIR 1.0, Flex 3, and BlazeDS just went out the door.
As part of this, we're officially open source!!! This is amazing to me, and I'm glad we're finally out there. Start checking out our source code (you already could), but also check out the compiler source code.
Lastly, I've started a more personal blog at <a href="http://frishycooks.blogspot.com">http://frishycooks.blogspot.com</a> . It's not just a blog about cooking, but it mostly will be.Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com0tag:blogger.com,1999:blog-3873498834654811872.post-87654130369213789272007-12-02T15:25:00.000-08:002007-12-11T10:41:20.966-08:00Making Flex Menus Easier<p style="font-family: arial;">The Flex framework makes your life a lot easier when creating an application. However, not everything is necessarily as easy as it should be right out of the box; however, there are extensibility points within the framework to make this job easier. One such particular example is Menus. So let's look at a quick example:</p>
<div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);">
<pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;"><?xml version="1.0" encoding="utf-8"?><br /><mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"><br /> <mx:MenuBar showRoot="false" menuShow="menuShowHandler(event)" itemClick="menuClickHandler(event)"><br /> <mx:XML><br /> <menuitem id="rootMenu"><br /> <menuitem label="File"><br /> <menuitem id="menuNew" label="New"/><br /> <menuitem label="Open"/><br /> </menuitem><br /> <menuitem label="Edit"><br /> <menuitem id="cut" label="Cut"/><br /> <menuitem id="copy" label="Copy"/><br /> <menuitem id="paste" name="paste" label="Paste" enabled="false"/><br /> <menuitem type="separator" /><br /> <menuitem label="Other menu item"/><br /> </menuitem><br /> <menuitem id="windowMenu" label="Window"><br /> <menuitem id="d1" label="Document 1" type="radio" group="docs" toggled="false"/><br /> <menuitem label="Document 2" type="radio" group="docs" toggled="true"/><br /> <menuitem id="d3" name="d3" label="Document 3" type="radio" group="docs" toggled="false"/><br /> <menuitem label="Document 4" type="radio" group="docs" toggled="false"/><br /> </menuitem><br /> </menuitem><br /> </mx:XML><br /> </mx:MenuBar><br /> <br /> <mx:Script><br /> <![CDATA[<br /> import mx.events.MenuEvent;<br /> <br /> private function menuClickHandler(event:MenuEvent):void {<br /> switch (event.label) {<br /> case "Cut":<br /> cutHandler(event);<br /> break;<br /> case "Copy":<br /> copyHandler(event);<br /> break;<br /> // many more cases if needed....<br /> }<br /> }<br /> <br /> private function menuShowHandler(event:MenuEvent):void {<br /> switch (event.label) {<br /> case "Edit":<br /> editHandler(event);<br /> break;<br /> // many more cases if needed....<br /> }<br /> }<br /> <br /> private function cutHandler(event:MenuEvent):void {<br /> trace("cut event");<br /> }<br /> <br /> private function copyHandler(event:MenuEvent):void {<br /> trace("copy event");<br /> }<br /> <br /> private function editHandler(event:MenuEvent):void {<br /> trace("edit event");<br /> }<br /> ]]><br /> </mx:Script><br /></mx:Application></pre>
</div>
<p style="font-family:Arial, Helvetica, sans-serif">So you look at that, and it's pretty damn easy to create a menu. You create a menu based off of a simple dataProvider that's written in an easy-to-understand XML format. So what's wrong with it? Well, I think there are a few problems:</p>
<ul style="line-height:normal">
<li style="font-family:Arial, Helvetica, sans-serif">The XML is untyped, so it's easy to make a mistake or forget what the properties are (type, not typed and toggle, not toggled, etc...). It's also easy to forget the values the properties take (for example: <span style="font-family:courier new;">type</span> takes <span style="font-family:courier new;">check</span>, <span style="font-family:courier new;">radio</span>, <span style="font-family:courier new;">separator</span> and not <span style="font-family:courier new;">checked</span>, <span style="font-family:courier new;">checkbox</span>, or something similar)</li>
<li style="font-family:Arial, Helvetica, sans-serif">Databinding is not supported, so to disable the cut, copy, and paste menu items, we'd have to access the <span style="font-family:courier new;">dataProvider</span> for all three menu items and change it manually. We'd have to do this in a special method and remember to do it everytime, whereas if data binding was supported, it'd be done for us automatically when one property changes (for example, when a textarea has focus or something)</li>
<li style="font-family:Arial, Helvetica, sans-serif">Events are global for the whole menu. It's much better to attach events at the most specific point possible because we can avoid large, monolithic functions that essentially just dispatch the event somewhere else based on a large switch-case statement. Let the "framework" handle that for us.</li>
</ul>
<p style="font-family:Arial, Helvetica, sans-serif">Ok, so those are atleast some of the problems that I have with the current state of menus and specifying the data provider. However, we can fix these problems pretty easily. Since our data provider can be an object (not just XML), we can take advantage of this because MXML really just compiles down into an ActionScript object. Because MXML is essentially a typed object, we solve problem #1 (it can even tell us that <span style="font-family:courier new;">type</span> accepts <span style="font-family:courier new;">check</span>, <span style="font-family:courier new;">radio</span>, or <span style="font-family:courier new;">seperator</span> as inputs). Since MXML supports databinding, we solve problem #2, and with some functions of our own, we can define events on our new MXML object and get it routed to these more specific events and solve problem #3.</p>
<p style="font-family:Arial, Helvetica, sans-serif">Here's what the example above will look like when done:</p>
<div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);">
<pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;"><?xml version="1.0" encoding="utf-8"?><br /><mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*"><br /> <mx:MenuBar showRoot="false" menuShow="menuItemEventHandler(event)" menuHide="menuItemEventHandler(event)"<br /> itemClick="menuItemEventHandler(event)" itemRollOut="menuItemEventHandler(event)"<br /> itemRollOver="menuItemEventHandler(event)" change="menuItemEventHandler(event)"><br /> <local:MenuItem id="rootMenu"><br /> <local:MenuItem label="File"><br /> <local:MenuItem id="menuNew" label="New"/><br /> <local:MenuItem label="Open"/><br /> </local:MenuItem><br /> <local:MenuItem label="Edit" menuShow="editHandler(event)"><br /> <local:MenuItem id="cut" label="Cut" itemClick="cutHandler(event)"/><br /> <local:MenuItem id="copy" label="Copy" itemClick="copyHandler(event)"/><br /> <local:MenuItem id="paste" name="paste" label="Paste" enabled="false"/><br /> <local:MenuItem type="separator" /><br /> <local:MenuItem label="Other menu item"/><br /> </local:MenuItem><br /> <local:MenuItem id="windowMenu" label="Window"><br /> <local:MenuItem id="d1" label="Document 1" type="radio" group="docs" toggled="false"/><br /> <local:MenuItem label="Document 2" type="radio" group="docs" toggled="true"/><br /> <local:MenuItem id="d3" name="d3" label="Document 3" type="radio" group="docs" toggled="false"/><br /> <local:MenuItem label="Document 4" type="radio" group="docs" toggled="false"/><br /> </local:MenuItem><br /> </local:MenuItem><br /> </mx:MenuBar><br /> <br /> <mx:Script><br /> <![CDATA[<br /> import mx.events.MenuEvent;<br /> <br /> private function menuItemEventHandler(event:MenuEvent):void<br /> {<br /> if (event.item is MenuItem)<br /> {<br /> EventDispatcher(event.item).dispatchEvent(event);<br /> } <br /> else if (event.menu && event.menu.dataProvider && <br /> event.menu.dataProvider[0] is MenuItem && <br /> event.menu.dataProvider[0].parent is MenuItem)<br /> {<br /> EventDispatcher(event.menu.dataProvider[0].parent).dispatchEvent(event);<br /> }<br /> }<br /> ]]><br /> </mx:Script><br /> <br /> <mx:Script><br /> <![CDATA[<br /> private function cutHandler(event:MenuEvent):void<br /> {<br /> trace("cut event");<br /> }<br /> <br /> private function copyHandler(event:MenuEvent):void<br /> {<br /> trace("copy event");<br /> }<br /> <br /> private function editHandler(event:MenuEvent):void<br /> {<br /> trace("edit event");<br /> }<br /> ]]><br /> </mx:Script><br /></mx:Application></pre>
</div>
<p style="font-family:arial;">You can see the code above looks basically the same, but it does solve all of our problems. We can even reference MenuItems by id's now, treating them as first-class objects. The only tricky part is the <span style="font-family:courier new;">menuItemEventHandler </span>defined that you'll also need. This grabs the appropriate <span style="font-family:courier new;">MenuItem</span> object and then dispatches the event to the event handler defined for that <span style="font-family:courier new;">MenuItem</span>. This is different then the switch-case statement above because it doesn't actually know anything about our menu data -- it just dispatched it through to the event defined on the menu item, so it's the same for all menus.</span></span></p>
<p style="font-family:arial;">So how was this so easily accomplished? Well it's with a new <span style="font-family:courier new;">MenuItem</span> class. It's basically just an object with some properites, like <span style="font-family:courier new;">type</span>, <span style="font-family:courier new;">enabled</span>, <span style="font-family:courier new;">name</span>, etc... and one important property, called <span style="font-family:courier new;">children</span> that's an array of <span style="font-family:courier new;">MenuItem</span>s. In its simplest form, <span style="font-family:courier new;">MenuItem</span> looks like:</p>
<div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);">
<pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal">package<br />
{<br />
[Event(name="change", type="mx.events.MenuEvent")]<br /> [Event(name="itemClick", type="mx.events.MenuEvent")]<br /> [Event(name="menuHide", type="mx.events.MenuEvent")]<br /> [Event(name="menuShow", type="mx.events.MenuEvent")]<br /> [Event(name="itemRollOut", type="mx.events.MenuEvent")]<br /> [Event(name="itemRollOver", type="mx.events.MenuEvent")]<br /> <br /> [DefaultProperty("children")]<br /> public class MenuItem extends EventDispatcher<br /> {<br /> public function MenuItem() {<br /> }<br /> <br /> [Bindable]<br /> public var enabled:Boolean = true;<br /><br/> [Bindable]<br /> public var toggled:Boolean;<br /> <br /> public var name:String = null;<br /> <br /> public var group:String = null;<br /> <br /> public var parent:MenuItem = null;<br /> <br /> public var label:String = null;<br /> <br /> [Inspectable(category="General", enumeration="check,radio,separator")]<br /> public var type:String = null;<br /> <br /> public var icon:Object = null;<br /><br/> private var _children:Array = null;<br /> <br /> [Inspectable(category="General", arrayType="MenuItem")]<br /> [ArrayElementType("MenuItem")]<br /> public function set children(c:Array):void<br /> {<br /> children = c;<br /> if (c)<br /> for (var i:int = 0; i < c.length; i++)<br /> c[i].parent = this;<br /> }<br /><br /> public function get children():Array<br /> {<br /> return _children;<br /> }<br /> <br /> // functions for manipulating children:<br /> }<br />}</pre>
</div>
<p style="font-family:arial;">Two cool things to note here are with the meta-data. The first is with <span style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;">[DefaultProperty("children")]</span> defined above the class. This tells the compiler that when children are added in MXML, what property it actually correlates to. Another example of this is in the Flex framework for List and Menu where <span style="font-family:courier new">dataProvider</span> is the default property. Another metadata trick is with <span style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;">[Inspectable(category="General", enumeration="check,radio,separator")]</span> where we tell Flex Builder what the type property accepts so that code-hinting works quite nicely.</p>
<p style="font-family:arial;">So the code above actually work perfectly fine, but I added some extra methods for dealing with children (adding, find, remove, etc...), which I will disclose aren't tested very well. I've uploaded the full <a href="http://cid-accf5592d884cf80.skydrive.live.com/self.aspx/Public/MenuItem.as">MenuItem.as</a> code so you can download it. Mike Schiff, another Flex SDK engineer, was the first one to point out this technique, and his code was the basis for everything here (in fact, it was probably most of it).</p>
<p style="font-family:arial;">This is a really simple technique but it helps with a few pits of creating menus in Flex. Not only that, but it can be applied to all components based on data providers. If you want to define a similar MXML object but want to have non-standard properties, like <span style="font-family:courier new;">myType</span> instead of <span style="font-family:courier new;">type</span>, you can tell the Flex framework this by using either <span style="font-family:courier new;"><a href="http://livedocs.adobe.com/flex/2/langref/mx/controls/MenuBar.html#dataDescriptor">dataDescriptor</a></span> or something like <span style="font-family:courier new;"><a href="http://livedocs.adobe.com/flex/2/langref/mx/controls/MenuBar.html#labelField">labelField</a></span>/<span style="font-family:courier new;"><a href="http://livedocs.adobe.com/flex/2/langref/mx/controls/MenuBar.html#labelFunction">labelFunction</a></span>.</p>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com25tag:blogger.com,1999:blog-3873498834654811872.post-8835666870163046902007-11-28T13:02:00.000-08:002007-11-28T13:06:10.467-08:00Flex Tutorial VideosI just found out about a really cool list of videos about Flex/Flex Builder. They are great for learning info, especially about the new features for Flex 3:
<a href="http://labs.adobe.com/wiki/index.php/Flex_3:Feature_Introductions">http://labs.adobe.com/wiki/index.php/Flex_3:Feature_Introductions</a>
As per blogging, I'll be posting some more code samples soon.Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com0tag:blogger.com,1999:blog-3873498834654811872.post-40199295908460283092007-10-20T11:19:00.002-07:002007-10-22T16:34:21.324-07:00Validation/Invalidation Mechanism<p><span style="font-size:100%;"><span style="font-family:arial;">It's been quite a while since I blogged last--for that I apologize. </span>
<span style="font-family:arial;">When you create a UI component in Flex, there are 4 main methods we ask that you implement: <span style="font-family:courier new;">createChildren</span>, <span style="font-family:courier new;">commitProperties</span>, <span style="font-family:courier new;">validateSize</span>, and <span style="font-family:courier new;">updateDisplayList</span>. The Flex framework then calls these methods at the appropriate times, but if you get into component development, you need to understand not only how these mechanisms work, but why we have these mechanisms in the first place. As a new member to the Flex SDK team, this was one of the first questions I had. To help answer this, let's take a look at something I'm currently implementing. Note that whatever I show may or may not end up being the final implementation, but it's a good example to look at.
In <a href="http://labs.adobe.com/technologies/air/">AIR </a>(Adobe Integrated Runtime), they added NativeMenu's, which are menus native to each operating system and sit at the top of the screen for a window. The API for this is centered around instantiating Menus, MenuItems, and adding them all together. A typical example would look like:
<div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);"><pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;">
var mainMenu:NativeMenu = new NativeMenu();
var fileMenu:NativeMenu = new NativeMenu();
var printItem:NativeMenuItem = new NativeMenuItem("Print");
printItem.keyEquivalent = "P";
printItem.keyEquivalentModifiers = [Keyboard.CONTROL];
printItem.mnemonicIndex = 0;
...
var saveItem:NativeMenuItem = new NativeMenuItem("Save");
...
var newItem:NativeMenuItem = new NativeMenuItem("New");
...
fileMenu.addItem(printItem);
fileMenu.addItem(saveItem);
fileMenu.addItem(newItem);
var fileMenuItem:NativeMenuItem = new NativeMenuItem("File");
fileMenuItem.subMenu = fileMenu;
mainMenu.addItem(fileMenuItem);
// repeat for Edit, View, etc...
</pre></div>
<span style="font-family:arial;">This can be a really long process. If there's anything Flex has taught us, it's that declarative markup can be a lot easier. If you look at the Flex interface, we use dataProviders to drive our menu. We want to do the same thing here for <span style="font-family:courier new;">NativeMenu</span>s. So the idea would be:
</span></span></span></p><div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);">
<pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;">[Bindable]
private var myMenuData:XMLList =
<>
<menuitem label="_File">yy
<menuitem label="_Save" keyEquivalent="F10" />
<menuitem label="P_rint" mnemonicIndex="4" mnemonicIndexOverride="true" />
<menuitem type="separator" />
<menuitem label="E_xit" />
</menuitem>
<menuitem label="_Edit">
<menuitem label="_Copy" toggled="true" type="check" />
<menuitem label="C_ut"/>
<menuitem label="_Paste" />
</menuitem>
</>;
<mx:Desktopmenu id="desktopMenu" dataProvider="{myMenuData}" labelField="@label" showRoot="false"/>
</pre></div>
<span style="font-size:100%;"><span style="font-family:arial;"><span style="font-family:arial;">Don't worry much about the XML format specified above (the underscores, how shortcuts are specified, etc...). That interface is up in the air, but the idea is what's important, and that is it's a lot easier to use XML to declaratively create the menu.
One of the odd things about this component is that it's not a <span style="font-family:courier new;">UIComponent</span>. What I mean by that is it won't extend <span style="font-family:courier new;">UIComponent </span>because we're not actually adding it to the underlying Flash display list. We're just creating <span style="font-family:courier new;">NativeMenuItem<span style="font-family:arial;">s</span></span><span style="font-family:arial;">,</span> where AIR ends up using the underlying operating system menu API (atleast that's what I think happens). Yet, despite not being a <span style="font-family:courier new;">UIComponent</span>, we can still discuss why validation/invalidation is an important concept.
Let's say someone wants to change the <span style="font-family:courier new;">dataProvider </span>for this menu. When they do this, we need to recreate the <span style="font-family:courier new;">NativeMenu </span>objects underneath. So let's say we have something like this:
<div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);"><pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;">public function set dataProvider(value:Object):void
{
_dataProvider = value;
createNativeMenu();
}</pre></div>
Where <span style="font-family:courier new;">createNativeMenu </span>loops through our <span style="font-family:courier new;">dataProvider </span>and recreates the underlying <span style="font-family:courier new;">NativeMenu</span>. Well we also need to do this if someone changes the <span style="font-family:courier new;">labelField</span>, <span style="font-family:courier new;">iconField</span>, <span style="font-family:courier new;">dataDescriptor</span>, or a few other properties (for many Flex classes, this list is even longer).
However, when you change the <span style="font-family:courier new;">dataProvider</span>, you oftentimes need to change other properties as well. For example, our new <span style="font-family:courier new;">dataProvider </span>might use a different field for it's label and icon. If after every setter, we re-created the menu, we'd be doing it too many times.
What we really want is something like a <span style="font-family:courier new;">commit</span>() function, where after making all the changes one wants, we then recreate the menu. We could do this, and in fact, we thought about doing this, but instead, let's do something really simple so that the programmer doesn't have to remember to do this. Let's just set a flag called <span style="font-family:courier new;">invalidatePropertiesFlag </span>and then wait 50 ms to re-create the menu.
</span></span></span><div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);"><pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;">public function set dataProvider(value:Object):void
{
_dataProvider = value;
invalidateProperties();
}
private invalidatePropertiesFlag:Boolean = false;
private function invalidateProperties()
{
if (!invalidatePropertiesFlag)
{
invalidatePropertiesFlag = true;
var myTimer:Timer = new Timer(100, 1);
myTimer.addEventListener(TimerEvent.TIMER, validateProperties);
myTimer.start();
}
}
private function validateProperties(event:TimerEvent)
{
if (invalidatePropertiesFlag)
{
invalidatePropertiesFlag = false;
createNativeMenu();
}
} </pre></div>
<span style="font-family:arial;">All right, so with this approach, we've successfully solved our problem. Now when multiple properties are set that invalidate our menu, we only end up recreating the menu once (not 3 times).
If this approach was applied throughout the framework, though, we'd have a few problems. Individual components would be validated at different times. However, the truth is that components are dependent on each other. For example, if the <span style="font-family:courier new;">DateField </span>component changes, it causes the underlying <span style="font-family:courier new;">DateChooser </span>component to change as well, so really we need a coordinated effort of validation. This is especially true for <span style="font-family:courier new;">UIComponent</span>s that are drawn on the screen, where changing the size of one component affects all the other components around it as well as all it's children. This is why we have <span style="font-family:courier new;">LayoutManager</span>. It coordinates this whole process
I won't get into the complete lifecycle here, but in a gist, <span style="font-family:courier new;">LayoutManger </span>calls <span style="font-family:courier new;">validateProperties</span>() on all the components in a top-down fashion. Then <span style="font-family:courier new;">validateSize</span>() is called bottom-up, essentially asking everyone how big they want to be. Lastly, <span style="font-family:courier new;">validateDisplayList</span>() is called which tells everyone how big they are going to <span style="font-family:courier new;">be </span>(they don't always get what they want), and this happens top-down. Underneath the hood, <span style="font-family:courier new;">UIComponent </span>and Container are base classes that implement these three methods. They do some extra work for you, and then they call <span style="font-family:courier new;">commitProperties</span>(), <span style="font-family:courier new;">measure</span>(), and <span style="font-family:courier new;">updateDisplayList</span>(unscaledWidth, unscaledHeight) respectively.
One thing to note is how <span style="font-family:courier new;">validateProperties</span>() and <span style="font-family:courier new;">validateDisplayList</span>() are top-down, while <span style="font-family:courier new;">validateSize</span>() is bottom-up. This is because properties usually propagate top-down (Example is <span style="font-family:courier new;">DateField </span>properties changing the underlying <span style="font-family:courier new;">DateChooser </span>component). </span><span style="font-size:100%;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><span style="font-family:courier new;">validateSize</span>() is bottom-up because we need to know how big our children are before we can determine our size. Lastly, </span></span></span></span></span></span><span style="font-size:100%;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><span style="font-family:courier new;">validateDisplayList</span>() is top-down because ultimately the parent decides how big each children gets to be (it usually tries to respect basic principles, like always listening to the children's <span style="font-family:courier new;">minWidth</span>/<span style="font-family:courier new;">minHeight</span>).
Besides these inter-dependencies between properties, there's another reason why we coordinate this effort. Underneath, the Flash Player essentially uses a timeline with frames. The player doesn't actually update it's display list until the next frame passes. So if I say <span style="font-family:courier new;">myComponent.x = 50;</span> the object doesn't actually move until this frame finishes. Here's an example that shows this:</span></span></span></span></span></span><div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);"><pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;"><?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="init()" layout="absolute" >
<mx:Script>
<![CDATA[
private function init():void
{
stage.frameRate = 1;
}
private function width50():void
{
myCanvas.width = 50;
var myTimer:Timer = new Timer(500, 1);
myTimer.addEventListener(TimerEvent.TIMER, width200);
myTimer.start();
}
private function width200(event:TimerEvent):void
{
myCanvas.width = 200;
}
]]>
</mx:Script>
<mx:Canvas id="myCanvas" backgroundColor="red" width="100" height="100" />
<mx:Button label="Click Me" click="width50()" x="200" y="200" />
</mx:Application></pre>
</div>
<p style="font-size:100%;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;">We change the framerate to 1 per second. Now, when you click the button, sometimes you'll see it shrink to a width of 50 before growing to 200, or sometimes you'll see it just go to 200. It depends on exactly when you click on the button with respect to where in the current frame we are.</span></span></span></span></span><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><div width="100%" align="center"><object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab" height="250" width="70%">
<param name="movie" value="http://www.skyalbum.com/album/Arielladog/2007-10-23/20071023043009.swf">
<param name="quality" value="high">
<param name="bgcolor" value="#869ca7">
<param name="allowScriptAccess" value="sameDomain">
<embed src="http://www.skyalbum.com/album/Arielladog/2007-10-23/20071023043009.swf" bgcolor="#869ca7" name="bug" play="true" loop="false" quality="high" allowscriptaccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer" align="middle" height="250" width="70%"></embed></object></div>
There are a few reasons the flash player does this as it helps with performance, but the important point is that Flex ties into this and does validation/re-measuring only when it'll actually affects the screen by tying in to the ENTER_FRAME event. So, in this DesktopMenu component, we should do the same thing and validate ourselves when all other components do (to be honest, I'm not sure if the native menu, since it's using the operating system API underneath can update itself in between frames or not). To do this, we need to implement <span style="font-family:courier new;">ILayoutManagerClient</span>, like <span style="font-family:courier new;">UIComponent </span>does, and then attach ourselves to the <span style="font-family:courier new;">LayoutManager</span>'s queue by calling <span style="font-family:courier new;">UIComponentGlobals.mx_internal::layoutManager.invalidateProperties(this);</span>. Since we're not a display object, we don't need to do anything in <span style="font-family:courier new;">validateSize </span>or <span style="font-family:courier new;">validateDisplayList</span>. One thing to note is that despite it's name, <span style="font-family:courier new;">LayoutManger </span>should really be though of as a <span style="font-family:courier new;">ValidationManager </span>because it doesn't just deal with layouts.
One other reason we do this is because sometimes the order of properties matters, and if it's represented as MXML, this order can be lost. So for example, in a <span style="font-family:courier new;">List </span>component, we might do something like:
</span></span></span></span></span>
</p><div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);">
<pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;">myList.dataProvider = ["a", "b", "c"]; // gets promoted to an ArrayCollection
myList.selectedIndex = 1;</pre>
</div>
<p><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;">But in MXML, you have:</span></span></span></span></span><span style="font-size:100%;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;">
</span></span></span></span></span></span>
</p> <p></p>
<span style="font-size:100%;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><span style="font-family:courier new;"><span style="font-family:arial;"><div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);">
<pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;"><mx:List id="myList" dataProvider="{['a', 'b', 'c']}" selectedIndex="1" /></pre>
</div>
or:
<div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);">
<pre style="color: rgb(0, 0, 153); font-size: 10pt; font-family: courier new; line-height: normal;"><mx:List id="myList" selectedIndex="1" dataProvider="{['a', 'b', 'c'}" /></pre>
</div>
You can see that the order actually matters here, but in XML we don't really have an order of operations. <span style="font-family:courier new;">commitProperties</span>() sorts all this out for us, and sets <span style="font-family:courier new;">dataProvider </span>before setting <span style="font-family:courier new;">selectedIndex</span>.
All right, that was a lot of code. I'm sure I could be missing some reasons, but to re-cap, why we have <span style="font-family:courier new;">LayoutManager </span>and this invalidation/validation scheme:</span></span></span></span></span></span>
<ul style="font-family:arial;"><li><span style="font-size:100%;">Properties tend to change at the same time, and we don't want to re-do the same work because of this.</span></li>
<li><span style="font-size:100%;">Property changes often cascade and affect many other properties (changing my position, would affect lots of other elements). This exacerbates the point made above.</span></li>
<li><span style="font-size:100%;">The Flash Player uses delayed rendering, so changing a UI property doesn't actually display until the next frame</span></li>
<li><span style="font-size:100%;">MXML representations of objects don't take into account order of operations.</span></li> </ul> <span style=";font-family:arial;font-size:100%;" >So I hope this helps explain the why behind the Flex scheme and not just the how.</span>
<p></p>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com4tag:blogger.com,1999:blog-3873498834654811872.post-75347667001650865032007-09-09T21:07:00.000-07:002009-03-20T18:14:44.274-07:00AutoScrolling for Flex TreeWe've been mostly fixing bugs on the SDK as we prepare for the release for MAX, and one of the bugs that was assigned to me was SDK-9645, <a href="https://bugs.adobe.com/jira/browse/SDK-9645">https://bugs.adobe.com/jira/browse/SDK-9645</a>.
The problem was that in a Tree (and other List-based components), when you set the horizontalScrollPolicy to auto, the scrollbars actually don't come out when they should. This seems like a bug at first, but we did this by design for performance reasons. In order to display the scrollbar properly, we need to measure the width of all the items (on-screen or not) and this would just take too much time to do by default. So instead, to get a scrollbar to show up, you need to set maxHorizontalScrollPosition, which is how much the user can scroll.
However, this can be annoying, especially as you might not know how big your data is. So I've created a simple component that extends Tree that measures the width of all the renderers so the scrollbars are calculated correctly.
In order to do this, in updateDisplayList, which is called whenever we need to redraw, I call measureWidthOfItems, which calculates the max width for all the item renderers. This means that redrawing takes some more time and is a little slower. Anyways, here's the code:
<div style="overflow: auto; width: 85%; margin-left: auto; margin-right: auto; background-color: rgb(235, 235, 235);">
<span style="color: rgb(0, 0, 153);font-family:courier new;font-size:10;" ><pre>package{
import flash.events.Event;
import mx.controls.Tree;
import mx.core.mx_internal;
import mx.core.ScrollPolicy;
import mx.events.TreeEvent;
public class AutoSizeTree extends Tree
{
public function AutoSizeTree(){
super();
horizontalScrollPolicy = ScrollPolicy.AUTO;
}
// we need to override maxHorizontalScrollPosition because setting
// Tree's maxHorizontalScrollPosition adds an indent value to it,
// which we don't need as measureWidthOfItems seems to return exactly
// what we need. Not only that, but getIndent() seems to be broken
// anyways (SDK-12578).
// I hate using mx_internal stuff, but we can't do
// super.super.maxHorizontalScrollPosition in AS 3, so we have to
// emulate it.
override public function get maxHorizontalScrollPosition():Number
{
if (isNaN(mx_internal::_maxHorizontalScrollPosition))
return 0;
return mx_internal::_maxHorizontalScrollPosition;
}
override public function set maxHorizontalScrollPosition(value:Number):void
{
mx_internal::_maxHorizontalScrollPosition = value;
dispatchEvent(new Event("maxHorizontalScrollPositionChanged"));
scrollAreaChanged = true;
invalidateDisplayList();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
// we call measureWidthOfItems to get the max width of the item renderers.
// then we see how much space we need to scroll, setting maxHorizontalScrollPosition appropriately
var diffWidth:Number = measureWidthOfItems(0,0) - (unscaledWidth - viewMetrics.left - viewMetrics.right);
if (diffWidth <= 0) maxHorizontalScrollPosition = NaN; else maxHorizontalScrollPosition = diffWidth; super.updateDisplayList(unscaledWidth, unscaledHeight); } } }</pre></span></div>
<b>[Code updated 3/20/09]</b>
Because we're doing this extra calculation, it means for a Tree with a lot of items, we may become slower. There are better ways of doing the same thing besides measuring every single renderer in updateDisplayList(), but this is definitely the easiest way. If performance is really a problem with this, let me know, and I'll try to come up with something a little smarter.
Below is an example, with the top one being the new AutoSizeTree component and the bottom one being the old one. Notice how the top is a little slower when resizing with an effect (this shows it's a little slower when laying out in general) when lots of the items are open.
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab" height="600" width="100%">
<param name="movie" value="http://sites.google.com/site/frishysblogupload/Home/main.swf">
<param name="quality" value="high">
<param name="bgcolor" value="#869ca7">
<param name="allowScriptAccess" value="sameDomain">
<embed src="http://sites.google.com/site/frishysblogupload/Home/main.swf" bgcolor="#869ca7" name="bug" play="true" loop="false" quality="high" allowscriptaccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer" align="middle" height="600" width="100%"></embed>
</object>
[Post updated on 3/20/09 to fix an issue: <a href="https://bugs.adobe.com/jira/browse/SDK-18499">SDK-18499</a>]Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com46tag:blogger.com,1999:blog-3873498834654811872.post-82619358972479501822007-09-09T20:00:00.000-07:002007-09-09T20:23:54.702-07:00Re-Introduction<p class="MsoNormal">For some technical reasons, I had to repost this introduction, but I'm also going to start blogging for real now. I have Internet at home, and we're getting a little more free time at work as we're almost done bug fixing for the next release. Anyways, on to the introduction.
</p> <p class="MsoNormal"><b style="">Who are you?<span style=""> </span></b>I am Ryan Frishberg, and I just graduated from Carnegie Mellon University.<span style=""> </span>In July, I moved out to the Bay Area and started work at Adobe on the Flex SDK team.<span style=""> </span>I’ve always been interested in software development and web technology, and Flex is absolutely awesome.
</p> <p class="MsoNormal"><b style="">Why are you blogging now?</b><span style=""> </span>Since I’ve tried to stay on top of the Web 2.0 and Rich Internet Application world, I read way too many blogs each day.<span style=""> </span>I found out that through all the jargon propaganda they give you (don’t worry, I’ll be dosing out similar content soon), the blogs are actually useful.<span style=""> </span>In fact, about half the time I Google for a technical question, I end up going to a blog.<span style=""> </span>So I hope that by starting this blog, I can contribute to some of the technical knowledge (as well as the proper jargon and propaganda) out there.<span style=""> </span>Also, I just started working on Flex, which means that I’ll have some really cool issues to talk about
</p> <p class="MsoNormal"><b style="">What will I talk about in the blog?</b><span style=""> </span>I would like to talk about web technologies, especially Flex. I’ll give examples on using Flex for beginners to show you how easy it is to create something useful in the framework. I also hope to cover more advanced topics that aren't covered as much by other blogs, like bugs I'm currently working on or limitations to the framework.</p> <p class="MsoNormal">Content is on its way soon, and if you want to reach me with any questions, feel free to contact me via email.
</p>Ryanhttp://www.blogger.com/profile/03770870265023271903noreply@blogger.com4