Learnosity Logo
Learnosity Banner Image

Migrating CSS from Flex 3 to Flex 4

Having searched around for how to make CSS work when migrating to Flex 4 from Flex 3 and finding lots of incorrect namespace declarations I thought I'd blog this as a reminder to myself:

If you have a current Flex3 project and you are migrating to Flex4 you need to add the following lines at the top of your stylesheet.

This makes the default non-namespaced items in your stylesheet refer to the MX components. eg:

Button{fontSize:18;}
And to style up spark buttons you simply use:
s|Button{fontSize:18;}

Note there were lots of ones that I found that were wrong.

Do NOT use:

This info was sourced from the Flex SDK Wiki.

Flex 4 RSL's and how to not use them

Flex 4 allows and defaults to using Runtime Shared Libraries (RSL's).

These have advantages of making flash movies using them work very well, but they can also require more http requests the first time they are used, and are not good for application development with Northcode.

To turn them off you need to add the following compiler flag:

-static-link-runtime-shared-libraries=true

Thanks to Flex Butterflies and bugs for the info.

ExtendoText project on google code

We've been busy squirrelling away on many projects and they are beginning to come to light:

One which is coming to light very early is a new text framework for actionscript which leverages flash 10 text engine.

It's very early alpha but is a good proof of concept and already solves some problems for us - namely the ability to simply create rich text area's that have superscript and subscript text.

It uses XML and CSS and completely seperates the formatting from the content.

Screenshot of extendotext 0.1

It can now:

  • Render a simple XML document
  • Apply styles including color, font and opacity
  • Plug in custom code for rendering images, video or any other XML tag you want to create.

The screenshot highlights a number of these features:

  • Bold, italic, super and subscript text
  • Text wrapping around custom display classes (Red and Green boxes)
  • Opacity applied to display objects (green box)

There is still a lot more to be done - including editing of text which is the next hard piece to knock over.

You can get it from the google code site

Keen to hear any feedback.

Cheers, Mark

More of AIR Gzip compression

Further to Marko's blog entry on implementing Gzip compression in AIR applications - turns out we were impressively hoodwinked.

By adding the 'Accept-Encoding':'gzip' header we were telling our apache server to serve up the compressed version of the content and we could see the content was compressed coming through Charles Proxy, and our application was working perfectly.

However, I started working on the exact same code the next day and straight away got an error - the application was erroring because it was getting binary data where it expected the decompressed XML data.

I turned Charles back on to see what was going on and it started working again. With my best sherlock holmes head on I rapidly deduced that Charles as well as proxying the request was also decompressing it helpfully for me.

Luckily I'd already come across the solution to the problem courtesy of Anirudh Sasikumar (GzipHTTPService) and Paul Robertson (Gzip Encoder).

I made some minor modifications to Anirudh's Gzip HTTP Service to do two things.

  • Always send the header 'Accept-Encoding':'gzip' if running a Desktop (Aka AIR) app.
  • Handle the case where the Gzip response that comes back is not gzip compressed. I.e. Charles already decrypted it or the server doesn't support gzip compression.
  • Change it to use version 0.2.0 of the gzip encoder

With these changes it is a drop in replacement for the <mx:HTTPService/> tag.

The changes are as follows:

...snip....
public function GzipHTTPService(rootURL:String=null, destination:String=null)
{
super(rootURL, destination);
         //If using AIR then set accept GZIP          if(Capabilities.playerType == "Desktop")
         {
            this.headers = {'Accept-Encoding':'gzip'};
         }
}

...snip....
else if ( body is ByteArray )
{
var barr:ByteArray = body as ByteArray;

try{
   var encoder:GZIPBytesEncoder = new GZIPBytesEncoder();
   /* decode the gzip encoded result */
   message.body = encoder.uncompressToByteArray(barr).toString();
}catch(error:IllegalOperationError){
   //Not gzip compressed - assume utf8    barr.position = 0;//reset to start of bytearray    message.body = barr.readUTFBytes(barr.length);
}
/* pass it on to HTTPService */
return super.processResult(message, token);            
}

...snip...

For convenience you can download the modified files from here.

Hope it helps.

Cheers, Mark

GZIP encoding in Adobe AIR/Flex - HOWTO

If you use HTTPService component in Flex or AIR to retrieve data from a server, you might want to consider gzipping the response. This can significantly reduce the packet size you get from the server, which results in a much better application performance.

This means that response packet is compressed by Apache web server and your client will need to decompress it. Fortunately, both Flex and AIR 1.5.1 natively support GZIP compression. However there are a couple of things you need to watch out for.

Firstly, you need to enable Apache Module mod_deflate in your virtual host.

In your AIR application, you need to tell HTTPService component to accept gzip encoding and AIR will take care of the rest:

<mx:HTTPService id="service" resultFormat="e4x" />
this.service.headers = {'Accept-Encoding':'gzip'};

If you are in a Flex project, specifying gzip encoding will throw the following error:

ArgumentError: Error #2096: The HTTP request header Accept-Encoding cannot be set via ActionScript.

This is because Flex apps run in a browser and the browser is responsible for determining encoding type and decompression (if required). Therefore, you cannot specify encoding type in Flex apps.

Our problem is that we use one core library for AIR and Flex applications. So the issue above can be avoided by conditionally specifying encoding type depending on the framework used. This can be done in 2 lines of code:

if(Capabilities.playerType == "Desktop")
this.service.headers = {'Accept-Encoding':'gzip'};

Note: this is not the complete solution - check out this entry for the rest of it :-)

Cheers Marko

AS3 BulkLoader example

I've been using AS3 BulkLoader for a while, which is great for loading multiple assets such as images and swfs into your application. Typical usage would look something like this:

instantiate BulkLoader object:

private var assetLoader:BulkLoader = new BulkLoader("instanceName");

add assets into the queue:

this.assetLoader.add("assets/myImage.jpg", {id:"key1"});
this.assetLoader.add("assets/myMovie.swf", {id:"key2"});
where id is the key, which is used to identify which asset to retrieve from the queue once it's been loaded.

To start loading assets you simply call the start() method:

this.assetLoader.start();

To get asset content, you would normally call getContent() method, which would return Bitmap for images or AVM1Movie for swf files. You could also call methods like getBitmap() and getAVM1Movie() to target specific file types.

The problem I encountered was that assets I was loading were encrypted. Therefore, I needed to read them in as ByteArray type and then decrypt them. Fortunately the latest version of BulkLoader has getBinary() method, but there is a trick to it. You need to tell BulkLoader to load assets in binary format when adding items into the queue:

this.assetLoader.add("assets/myImage.jpg", {id:"key1", type:BulkLoader.TYPE_BINARY});
this.assetLoader.add("assets/myMovie.swf", {id:"key2", type:BulkLoader.TYPE_BINARY});
And after all assets have been loaded, you get binary content:
var assetBinary1:ByteArray = this.assetLoader.getBinary("key1");
var assetBinary2:ByteArray = this.assetLoader.getBinary("key2");
Without specifying the type, BulkLoader will try to load data in text format which will result in the following error:
[BulkLoader] Failed to get content with url: 'file:///myFile.jpg'as type: [class ByteArray]

Setting the type to binary did the trick for me, so I thought I'd share it in case someone else falls into the same trap.

Oh, and I should thank Mark Lynch for helping me figure this out :-)

Marko

Can't Launch Air Apps after upgrading Flex Builder to 3.0.2

I recently upgraded flex builder to 3.0.2 to play with the new Flash 10 goodness and came across this little problem.

Whenever I clicked run it would just sit there for ages and never launch the app. If I clicked debug I got the following error:

Process terminated without establishing connection to debugger.

Command:

"C:\Program Files\Adobe\Flex Builder 3\sdks\3.2.0\bin\adl.exe" "C:\Documents and Settings\markl\My Documents\Flex Builder 3\SampleApp\bin-debug\SampleApp-app.xml" "C:\Documents and Settings\markl\My Documents\Flex Builder 3\DexterAir\bin-debug"

Output from command:

error while loading initial content

Turns out the solution is easy when you know how, thanks to this post.

The solution is a simple as changing the namespace in the app.xml file from:

to

Which is fine if you want to upgrade you app to 1.5 - not sure if there is still a way to compile apps for AIR 1.0 though.

Cheers, Mark Lynch

Flash Text Layout Framework - Getting Started

View Demo
Download Code

I finally got some time to play with Flash 10 and the new text functionality including the Text Layout Framework Beta

It gives very powerful control of the text layout but with this power come a few gotcha's which can trip you up.

The basic process for using the TextFlow for advanced text rendering is as follows:

  • Create a sprite container to hold the text
  • Import the text into a TextFlow object
  • Attach the container to the textflow object
  • Call updateAllContainers() on the textflow object
  • We also need to add event handlers for resize and graphics loading if we are using these features.

Create a sprite

_container = new Sprite();
this.textArea.rawChildren.addChild(_container);
this.textArea.addEventListener(ResizeEvent.RESIZE,resizeEvent);
Create a new sprite which get's attached to the canvas we've already created.
<mx:Canvas id="textArea" top="10" left="10" right="10" bottom="10"/>

Import the text into a TextFlow object

XML.ignoreWhitespace = false;         

var markup:XML = <TextFlow fontSize="30" fontFamily="Arial" whiteSpaceCollapse="preserve">
   <p fontSize="50" textAlign="center">Text Layout Framework Demo</p>
   <p>Text is now easy to control in Flash</p>
   <p><span>It can be formatted easily to do</span><span fontWeight="bold" whiteSpaceCollapse="preserve"> Bold</span><span>, </span><span fontStyle="italic">Italic</span><span>, </span><span textDecoration="underline">Underline</span><span> and </span><span lineThrough="true"> Strikethrough</span></p>
   <p>It can also finally do <span fontSize="20" alignmentBaseline="ideographicTop">Superscript</span> and <span fontSize="20" alignmentBaseline="ideographicBottom">Subscript</span></p>
   <p><a href='http://www.learnosity.com/techblog/'>Links</a> and inline images <img align="right" source="http://www.adobe.com/shockwave/download/images/flashplayer_100x100.jpg" /> are also available</p>
</TextFlow>;

_textFlow = TextFilter.importToFlow(markup,TextFilter.TEXT_LAYOUT_FORMAT);

This loads the XML and stores it in the _textFlow object.

Note: the XML.ignoreWhitespace is important otherwise it all the whitespace between tags will get stripped out and any formatted text will lose it's spacing.

Attach the container to the textflow object

_textFlow.flowComposer.addController(new DisplayObjectContainerController(_container,textArea.width,textArea.height));

Call updateAllContainers() on the textflow object

_textFlow.flowComposer.updateAllContainers();

That's it.

For bonus points you can add the event handlers to handle graphics loading and resize etc.

this.textArea.addEventListener(ResizeEvent.RESIZE,resizeEvent);
_textFlow.addEventListener(StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGED,graphicStatusChangeEvent);
And the functions they call look like this:
public function graphicStatusChangeEvent(evt:Event):void
      {
         // the graphic has loaded (or failed to load) update the display          _textFlow.flowComposer.updateAllContainers();
      }   
      public function resizeEvent(evt:Event):void
      {
         //Resize event - recreate the flowcompser with the correct width and height          _textFlow.flowComposer.removeAllControllers();   
         _textFlow.flowComposer.addController(new DisplayObjectContainerController(_container,textArea.width,textArea.height));
         _textFlow.flowComposer.updateAllContainers();
      }

Make it selectable

To make the text selectable we just need to add an interaction manager. As easy as this:
//To make selectable
_textFlow.interactionManager = new SelectionManager();

I'm looking forward to implementing the rest of this in our projects that use flash text. It's so much simpler and cleaner.

Cheers, Mark

"Logical AND" with flex binding in mxml

While making a small change to a view in one of our flex app's I came across the following little problem.

Initially there was a single parameter binding which set the button enabled, eg:

<mx:Button enabled="{model.user.allowSomething}" label="Do Something">

The business requirements changed and we need to have a global setting as well as a user setting, so now the setting had to be globally enabled and the user had to have access to it.

My 1st attempt was this:

<mx:Button enabled="{model.settings.allowSomething && model.user.allowSomething}" label="Do Something">

However, this gave the following error:

The entity name must immediately follow the '&' in the entity reference.

This you may recognise as an XML formatting error. After a quick google I came across a bug in the adobe bug tracker related to this (BTW I love that this is now open and searchable) which explained the workaround. XML escape the characters - which feels ugly when writing code, but it does make sense.

So the code now becomes:

<mx:Button enabled="{model.settings.allowSomething &amp;&amp; model.user.allowSomething}" label="Do Something">

I also came across another way of handling it using the ternary operator which is very clever but I think it is even less readable than the escaped XML characters.

Cheers, Mark

Flex/AIR embedded fonts - Exception during Transcoding - Solved

I've come across this error a number of times:

exception during transcoding: Unexpected exception encountered while reading font file '/C:/Documents and Settings/markl/My Documents/Flex Builder 3/DexterNorthCode/src/assets/DejaVuSans-Bold.ttf'

It can be very frustrating as there is not much information to be gleaned from the error messages and the whole font area is very murky and complicated.

There are a number for reasons that this error will occur

  • If you reference the wrong file
  • If you specify the fontStyle or fontWeight wrong
  • or most frustratingly if the font manager can't interpret the font correctly.

After grappling with this problem for I while and after much reading I realised that there are 3 different "font managers" that transcode fonts in AS3. These are:

  • flash.fonts.JREFontManager
  • flash.fonts.AFEFontManager
  • flash.fonts.BatikFontManager

The different font managers have different capabilities and should be automatically selected to choose the best one. However, this is not always the case.

You can force the compiler to use a specific manager by appending the following to the compiler arguments.

Project->Properties->Flex Compiler->Additional compiler arguments:

-managers=flash.fonts.AFEFontManager

This forces the use of the AFEFontManager which seems to have the best support for fonts and is the one being most actively developed.

More Entries