Wednesday, December 22, 2010

Drawing 3D Shapes in Flex

I found this great 3-part article by Lee Burrows on how to draw semi 3D shapes in Flex:
  • Part 1
  • Part 2
  • Part 3

  • I highly recommend that you check them out.

    Expanding on those examples I've changed them slightly in the following ways:
  • Instead of rotating the shape based on mouse position, the rotation now happens when you drag your mouse
  • Added a 3D Triangle and a 3D Arrow (combines a triangle and a cube)
  • Added a 3D shape with 6 different hockey logos, one on each side


  • All credit goes to Lee for his excellent examples. If you want to understand the math behind it all, then definitely read his article. My examples don't have much in the way of comments.

    View Source is available for all the following examples, right click below to access the source.

    Example #1 - Simple Rotating Cube:


    Example #2 - Rotating Triangle:


    Example #3 - Rotating Arrow (with highlighting):


    Example #4 - Cube With Image/Bitmap Fill:

    You might notice a few tiny problems with the arrow. When rotating it in certain directions some lines show through that shouldn't be visible. It is most likely due to combining the two shapes (one cube and one triangle) to form the arrow.

    Also, the mouse dragging to rotate works as expected until you've rotated the shape 180 degrees, then it becomes backwards. I haven't spent the time to try and fix this.

    The fourth example uses Lee's Shape3D class which encapsulates the 3D functionality in one simple class. It is much cleaner this way.

    Thursday, November 25, 2010

    YouTube Videos in Flex

    Here is an example of how to get YouTube videos to be shown inside your Flex Application.

    The first thing to do is add an SWFLoader to your mxml file. The source property of the SWFLoader gets set to the url of the video that you want to show inside Flex.
    But you can't just use any YouTube url like this one:
    http://www.youtube.com/watch?v=owGykVbfgUE
    Instead you use a url in this form:
    http://www.youtube.com/v/VIDEO_ID?version=3

    The id of the video you want to play (e.g. owGykVbfgUE) must replace the VIDEO_ID portion of the url. This url actually redirects to an SWF file somewhere.

    The version=3 parameter is very important. This tells YouTube to load the ActionScript 3.0 version of the SWF file. Without this parameter it defaults to the AS2 version which doesn't not play nicely with AS3. I found that I could watch one video, but after that no other videos could be played (I found this blog post that explains why if you're interested). I also found this YouTube reference which is where I learned about using the version=3 parameter.

    There are a few other parameters that you can use like:
    • autoplay=1 - starts the video immediately without the user having to press play
    • fs=1 - allows full screen
    • rel=1 - shows related content after the video has finished playing
    For a full list of the parameters go here

    And finally, when you want to play another video, make sure you call the swfLoader.unloadAndStop() function otherwise you can get multiple videos playing at the same time, very confusing.

    Keep in mind that when you run this locally you will see a lot of SecurityExceptions in your console, this is expected and normal.

    And one last thing, some videos you will not be able to play inside Flex due to copyright restrictions. The owners of youtube videos must be able to specify whether the video can be played from a different website using an embedded player.

    Here is the example in action (right click to view-source):

    Wednesday, October 27, 2010

    Displaying Html text in Labels

    Displaying html content inside Flex labels is not as straight forward as it was in Flex 3 by simply setting the htmlText property. Here are the 3 Spark controls used for displaying text:
    • Label - lightweight plain text only
    • RichText - as the name says it provides support for rich text.
      Also allows html content to be imported (anchor tags <a> don't work)
    • RichEditableText - provides the same rich text support as the RichText control.
      Allows editing and anchor html tags work properly
    Here is a list of the supported HTML tags: <div>, <p>, <a>, <span>, <img>, <br>.

    As noted above, if you want to use anchor <a> tags, you must use a RichEditableText control. In this case you'd most likely want to also set the editable="false" property.
    The anchor href property can be set to a relative path like
    <a href="index.html">index</a>
    Or an absolute one like
    <a href="http://google.com" target="_blank">google</a>
    Note that you can set the target property to control whether the link is opened in a new window or not.
    If you want to listen for when the user clicks on an anchor it is possible, but involves a lot more work. Basically you import the html string into a TextFlow object. Then you iterate through all the child elements until you find the LinkElement (which represents the anchor tag), and then add a FlowElementMouseEvent.CLICK event listener.

    Here is some sample code that I use to achieve this:
    /**
     * Converts the html string (from the resources) into a TextFlow object
     * using the TextConverter class. Then it iterates through all the 
     * elements in the TextFlow until it finds a LinkElement, and adds a 
     * FlowElementMouseEvent.CLICK event handler to that Link Element.
     */
    public static function addLinkClickHandler(html:String, 
            linkClickedHandler:Function):TextFlow {
      var textFlow:TextFlow = TextConverter.importToFlow(html, 
        TextConverter.TEXT_FIELD_HTML_FORMAT);
      var link:LinkElement = findLinkElement(textFlow);
      if (link != null) {
        link.addEventListener(FlowElementMouseEvent.CLICK, 
              linkClickedHandler, false0true);
      else {
        trace("Warning - couldn't find link tag in: " + html);
      }
      return textFlow;
    }

    /**
     * Finds the first LinkElement recursively and returns it.
     */
    private static function findLinkElement(group:FlowGroupElement):LinkElement {
      var childGroups:Array = [];
      // First check all the child elements of the current group,
      // Also save any children that are FlowGroupElement
      for (var i:int = 0; i < group.numChildren; i++) {
        var element:FlowElement = group.getChildAt(i);
        if (element is LinkElement) {
          return (element as LinkElement);
        else if (element is FlowGroupElement) {
          childGroups.push(element);
        }
      }
      // Recursively check the child FlowGroupElements now
      for (i = 0; i < childGroups.length; i++) {
        var childGroup:FlowGroupElement = childGroups[i];
        var link:LinkElement = findLinkElement(childGroup);
        if (link != null) {
          return link;
        }
      }
      return null;
    }

    A simple control that I've created and use frequently is called HtmlLabel which extends the RichEditableText class to provide simple support for html text:
    <?xml version="1.0" encoding="utf-8"?>
    <s:RichEditableText xmlns:fx="http://ns.adobe.com/mxml/2009"
      xmlns:s="library://ns.adobe.com/flex/spark"
      xmlns:mx="library://ns.adobe.com/flex/mx"
      focusEnabled="false"
      selectable="false"
      editable="false">

      <fx:Script>
        <![CDATA[
          import flashx.textLayout.conversion.TextConverter;

          override public function set text(value:String):void {
            super.textFlow = TextConverter.importToFlow(value, 
              TextConverter.TEXT_FIELD_HTML_FORMAT);
          }
        ]]>
      </fx:Script>

    </s:RichEditableText>

    Spell Checking

    Here is a short example of how to get the Adobe Squiggly spell checking tool installed. Squiggly can be downloaded here.

    Squiggly files:
    • AdobeSpellingConfig.xml - put this file in your project's src folder. It defines the locations of the dictionaries and which language(s) to use.
    • en_US.aff, en_US.dic - these are the dictionary files. You'll need different ones for different languages of course. By default they go in the src/dictionaries/en_US folder.
    • AdobeSpellingEngine.swc - the main engine library - required, goes in project lib folder.
    • AdobeSpellingUI.swc - the ui library for Flex 3/MX components (e.g. <mx:TextArea>)
    • AdobeSpellingUIEx.swc - the ui library for Flex 4/Spark components (e.g. <s:TextArea>)
    ** Only use one of AdobeSpellingUI.swc or AdobeSpellingUIEx.swc.

    To integrate Spell Checking into a Spark TextArea (id="textArea"), all you have to do is add one line:

    SpellUI.enableSpelling(textArea, "en_US");

    I did notice one problem with using Squiggly with Spark controls. The default Cut/Copy/Paste/Select All context menu items were not available anymore. I found a workaround for this by getting the contextMenu (which gets set on the RichEditableText control, not the TextArea) and setting clipboardMenu = true. See the example below for more details (right click to View Source).

    Squiggly is still in prerelease, so a few bugs are expected. There is a forum here for discussions on Squiggly, and to report problems.

    Thursday, September 9, 2010

    Flex 4 Tree Lines

    I've updated my previous blog post on Tree Lines to include a Flex 4/Spark version which uses the MXTreeItemRenderer class.

    For more documentation on how to use it see the blog post linked above.


    I've also updated the AdvancedDataGrid example to show tree lines in the tree: