Custom Preloader class for Flex apps

Actionscript, Flex No Comments »

I’ve blogged on this before but here’s an updated version of a custom preloader class that extends Sprite and loads in a MovieClip from a SWC to use as the animation and display the load progress.

Custom preloader for Flex

  1. package com.cravens.nr.preloader
  2. {
  3.         import com.cravens.nr.model.Config;
  4.        
  5.         import flash.display.GradientType;
  6.         import flash.display.Sprite;
  7.         import flash.events.Event;
  8.         import flash.events.ProgressEvent;
  9.         import flash.events.TimerEvent;
  10.         import flash.geom.Matrix;
  11.         import flash.utils.Timer;
  12.        
  13.         import mx.events.FlexEvent;
  14.         import mx.preloaders.IPreloaderDisplay;
  15.  
  16.         public class NRPreloader extends Sprite implements IPreloaderDisplay
  17.         {
  18.                 protected var loadingPanel:BusyPanel;
  19.                 protected var timer:Timer;
  20.                
  21.                 public function NRPreloader()
  22.                 {
  23.                         super();
  24.                 }
  25.                 //------------------------------------------------------------------------------
  26.                 public function set preloader(preloader:Sprite):void
  27.                 {
  28.                         preloader.addEventListener(ProgressEvent.PROGRESS, handleProgress);
  29.                         preloader.addEventListener(Event.COMPLETE, handleComplete);
  30.                        
  31.                         preloader.addEventListener(FlexEvent.INIT_PROGRESS, handleInitProgress);
  32.                         preloader.addEventListener(FlexEvent.INIT_COMPLETE, handleInitComplete);
  33.                 }
  34.                 //------------------------------------------------------------------------------
  35.                 public function initialize():void
  36.                 {
  37.                         var matrix:Matrix = new Matrix(0,1,1,0,0,0);
  38.                        
  39.                         graphics.beginGradientFill(GradientType.LINEAR,[0x000000,0x89265a],[1,1],[125,205],matrix);
  40.                         graphics.drawRect(0,0,Config.VIEWPORT_WIDTH,Config.VIEWPORT_HEIGHT);
  41.                        
  42.                         loadingPanel = new BusyPanel();
  43.                        
  44.                         try
  45.                         {
  46.                                 loadingPanel.heading_txt.text = "LOADING ... PLEASE WAIT";
  47.                                 loadingPanel.message_txt.text = "0/100%";
  48.                         }
  49.                         catch(e:Error)
  50.                         {
  51.                                 throw new Error("Asset does not contain the relevant textField objects");
  52.                         }
  53.                         finally
  54.                         {
  55.                                 loadingPanel.x = Config.VIEWPORT_CENTER_X-150;
  56.                                 loadingPanel.y = Config.VIEWPORT_CENTER_Y;
  57.                                
  58.                                 addChild(loadingPanel);
  59.                         }
  60.                 }
  61.  
  62.                
  63.                 //------------------------------------------------------------------------------
  64.                 // Define empty event listeners.
  65.                 private function handleProgress(event:ProgressEvent):void
  66.                 {
  67.                         var prog:Number = Math.ceil((event.bytesLoaded/event.bytesTotal)*100);
  68.                        
  69.                         loadingPanel.message_txt.text = prog+"/100%";
  70.                 }
  71.                 //------------------------------------------------------------------------------
  72.                 private function handleComplete(event:Event):void
  73.                 {
  74.                         loadingPanel.message_txt.text = "LAUNCHING";
  75.                 }
  76.                 //------------------------------------------------------------------------------
  77.                 private function handleInitProgress(event:FlexEvent):void
  78.                 {
  79.                 }
  80.                 //------------------------------------------------------------------------------
  81.                 private function handleInitComplete(event:Event):void
  82.                 {
  83.                         timer = new Timer(500,1);
  84.                         timer.addEventListener(TimerEvent.TIMER, dispatchComplete);
  85.                         timer.start();     
  86.                 }
  87.                
  88.                 //------------------------------------------------------------------------------
  89.                 private function dispatchComplete(event:TimerEvent):void
  90.                 {
  91.                         timer.removeEventListener(TimerEvent.TIMER, dispatchComplete);
  92.                         timer = null;
  93.                         dispatchEvent(new Event(Event.COMPLETE));
  94.                 }
  95.                
  96.                 //------------------------------------------------------------------------------
  97.                 // Implement IPreloaderDisplay interface
  98.                
  99.                 public function get backgroundColor():uint
  100.                 {
  101.                         return 0;
  102.                 }
  103.                 //------------------------------------------------------------------------------
  104.                 public function set backgroundColor(value:uint):void
  105.                 {
  106.                
  107.                 }
  108.                 //------------------------------------------------------------------------------
  109.                 public function get backgroundAlpha():Number
  110.                 {
  111.                         return 0;
  112.                 }
  113.                 //------------------------------------------------------------------------------
  114.                 public function set backgroundAlpha(value:Number):void
  115.                 {
  116.                 }
  117.                 //------------------------------------------------------------------------------
  118.                 public function get backgroundImage():Object
  119.                 {
  120.                         return undefined;
  121.                 }
  122.                 //------------------------------------------------------------------------------
  123.                 public function set backgroundImage(value:Object):void {
  124.                 }
  125.                 //------------------------------------------------------------------------------
  126.                 public function get backgroundSize():String {
  127.                         return "";
  128.                 }
  129.                 //------------------------------------------------------------------------------
  130.                 public function set backgroundSize(value:String):void {
  131.                 }
  132.                 //------------------------------------------------------------------------------
  133.                 public function get stageWidth():Number
  134.                 {
  135.                         return Config.VIEWPORT_WIDTH;
  136.                 }
  137.                 //------------------------------------------------------------------------------
  138.                 public function set stageWidth(value:Number):void
  139.                 {
  140.                 }
  141.                 //------------------------------------------------------------------------------
  142.                 public function get stageHeight():Number
  143.                 {
  144.                         return Config.VIEWPORT_HEIGHT;
  145.                 }
  146.                 //------------------------------------------------------------------------------
  147.                 public function set stageHeight(value:Number):void
  148.                 {
  149.                 }
  150.         }
  151. }

Also, notice I’ve created a simple grad background using the Sprite graphics …

  1. var matrix:Matrix = new Matrix(0,1,1,0,0,0);  
  2. graphics.beginGradientFill(GradientType.LINEAR,[0x000000,0x89265a],[1,1],[125,205],matrix);
  3. graphics.drawRect(0,0,Config.VIEWPORT_WIDTH,Config.VIEWPORT_HEIGHT);

This is the best way to do things because remember, the framework has not loaded while the preloader is displayed so you need to keep things lightweight and simple.

Also, the Matrix object rotates the grad so it runs vertically.

Have fun.

Detecting when text has run into the end of a container in a TextFlow object

Actionscript, Flex 3 Comments »

More fun and games with the Text Layout Framework. So, I have a load of links being flowed one by one through a bunch of linked containers. I need to detect when a link reaches the end and has ‘overflowed’ so to speak.

To do this you need to make sure your ContainerController objects have their ScrollPolicy set to OFF so the flowComposer recognises that the text is not in the container anymore. This will not halt the flow however so you need to add a nice bit of code that will detect if the FlowElement is no longer within a container.

Here’s the code …

Detecting when text has run into the end of the ContainerController in a TextFlow object

  1. /*
  2. A good idea to set a config object to pass into the TextFlow object when you create it
  3. */
  4. config = Configuration(TextFlow.defaultConfiguration).clone();
  5.  
  6. /*
  7. And add this overflowPolicy to it as well as your formatting defaults
  8. */
  9. config.overflowPolicy = OverflowPolicy.FIT_DESCENDERS;
  10.  
  11. textFlow = new TextFlow(config);
  12.  
  13. /*
  14.   Create your containers from sprites and make sure the scrollPolicy is OFF
  15. */
  16. var controller:ContainerController = new ContainerController(containerSprite,width,height);
  17. controller.verticalScrollPolicy = controller.horizontalScrollPolicy = ScrollPolicy.OFF;
  18.                                        
  19. textFlow.flowComposer.addController(controller);
  20.  
  21. /*
  22. Now imagine we are in a loop which adds a paragraph of lorem ipsum to the textflow and updates the controllers and checks if the flow is now out of the bounds of the container
  23. */
  24.  
  25. while(LOOP)
  26. {
  27.   textFlow.addChild(myParagraphElement); // you've created the paragraph
  28.   textFlow.flowComposer.updateAllControllers(); // you've created the controllers
  29.  
  30.   var containerIndex:int = textFlow.flowComposer.findControllerIndexAtPosition(textFlow.textLength-1);
  31.   if(containerIndex==-1) textFlowCompleteHandler();
  32. }

It’s these two lines that do the detection …

  1.  
  2. var containerIndex:int = textFlow.flowComposer.findControllerIndexAtPosition(textFlow.textLength-1);
  3.  
  4.   if(containerIndex==-1) textFlowCompleteHandler();

The findControllerIndexAtPosition method will return -1 if the character index passed in is not within a container. Remember to use textFlow.textLength-1 to get the last character. The textFlow variable is an instance of the TextFlow object.

The textFlowCompleteHandler can be any method that stops the insertion of FlowElement objects happening.

Adobe vs Apple? Sod the developers … what does the user want?

Apple, Flash, iPhone, rant, Web tools No Comments »

There’s been all kinds of heated debate and colorful invective surrounding the latest events in the Adobe vs Apple altercation. It’s hard for me to take sides because as a Flash developer for over ten years now and as a loyal Mac user for nearly 20 I’m finding that I do sympathise with both sides.

I find it distasteful that Apple should introduce section 3.3.1 merely days before the release of CS5 and Adobe’s new ‘compile Flash to iPhone app’ feature. It’s really quite underhand and will effect a lot of people’s livelihoods and damage business. Let’s not forget it’s not just Adobe that will be affected by this change. Unity3D and other 3rd party tools will not be able to release apps on the store. Some great apps like the ‘Star Wars Trench Run’ may need to be removed and as I understand it with my limited knowledge of these things some of the larger publishers like EA may also be affected as their games use 3rd party interpreters and tools that will be outlawed by Apple’s new rules.

On the other side though, Adobe should never have introduced this feature without the approval of Apple. They rushed this thing out and in the process have gambled with the livelihoods of hundreds of developers who have put their stake in iPhone app development with Flash.

Basically I think both parties have been very arrogant and have damaged a lot of reputations in the industry … mainly developers. We’ve got decent, honest people at each others throats when all they want to do is work together to make amazing, engaging content.

But the biggest victim in all this is the user. People buy iPhones and wonder why they can’t see the rich content they’ve come to expect. They struggle trying to view Flash content in the browser that hasn’t been built properly or they haven’t got the correct plug-in installed. What does the user want? Why is it all going wrong?

Fifteen years ago I was building CD-Rom titles in Director 3 which at the time was owned by Macromedia. We started using Flash 2 when it was released so we could include animated SWF content in our Director titles. We then started building Flash content for the web. At the time there was no such thing as ‘web standards’ … HTML was pretty basic and browsers were packed full of plug-in’s developed so you get the most out of your browsing experience. You had the ‘Real’ or ‘Quicktime’ plugin to watch video, ‘Shockwave’ allowed you to play amazing games online and ‘Flash’ exploded all over the web because it brought the browser to life and allowed us to build truly engaging rich content without loading new pages to update content.

In the present day we have a completely different type of internet. Web standards have evolved to the point that we now have a perfectly reasonable alternative to Flash integrated into the browser. HTML5 and WebGL allows us to build rich engaging content without a plug-in. In fact Flash is probably one of the few plugins people are still required to install into their browser to view a vast majority of content. And this is where Adobe have failed the user. Director died a death because people didn’t need to publish CD-Roms anymore and Flash should have evolved so the user didn’t need to use a plug-in in their browser.

Adobe should have adapted Flash so it output HTML5 and WebGL 3D content. The AIR runtime is fine on the desktop (although I will always choose a native Mac OS app) and will be just fine on the Android platform, hell they might even integrate it into their OS. But it’s in the browser they’ve failed and if they want to see Flash content on the iPhone they need to improve their tools (like Flash or Dreamweaver) so they produce standards compliant, rich, interactive content in the browser natively. This, after all, is what the user wants.

And Steve, stop being so evil. You’re forgetting what Apple was supposed to stand for. You should watch your 1984 Keynote speech every morning to remind yourself.

Some links on the subject …

http://www.smashingmagazine.com/2010/04/12/the-gradual-disappearance-of-flash-websites/

http://www.devwhy.com/blog/2010/4/12/its-all-about-the-framework.html?lastPage=true#comment8034519

http://mashable.com/2010/04/10/steve-jobs-adobe/

http://theflashblog.com/?p=1888

http://blog.codecomputerlove.com/2010/04/09/have-apple-crushed-cs5-flash-to-iphone-opportunity/

UPDATE: Apparently Flash to HTML5 was showcased during a ‘sneak peek’ session at MAX 2009 but has not made it into the new CS5 release. This is a real shame … Adobe could have made some real progress there.

UPDATE UPDATE: Apple have already started pulling apps. Scratch is an education tool built by MIT … it’s now not educating children in computer science.

Apple also banned this app for being too satirical!

Crazy code

Actionscript, Flex No Comments »

Sometimes you have to write a method for something crazy complicated. In this snippet I had a container with a load of TextFlow text in it and I needed to zoom into a specific link within the flow while also scaling the container and positioning it within a Scroller viewport without the container going out of the bounds of the Viewport when the link is centered within it.

This is meaningless to anyone but me but sometimes you just have to share the pain.

Zoom in to a link in a TextFlow within a scaled container in a Scroller viewport

  1. private function moveAndZoomToLink(link:LinkElement,p:Person):void
  2.                         {
  3.                                 var linkStart:int = link.getAbsoluteStart();
  4.                                 var textFlowLine:TextFlowLine = textFlow.flowComposer.findLineAtPosition(linkStart);
  5.                                 var linkPositionOnLine:int = linkStart-textFlowLine.absoluteStart;
  6.                                        
  7.                                 var controllerIndex:int = textFlow.flowComposer.findControllerIndexAtPosition(linkStart);
  8.                                 var controller:ContainerController = textFlow.flowComposer.getControllerAt(controllerIndex);
  9.                                 var containerSprite:Sprite = controller.container;
  10.                                
  11.                                 var rect:Rectangle = textFlowLine.getBounds();
  12.                                 var atomRect:Rectangle = textFlowLine.getTextLine().getAtomBounds(linkPositionOnLine+1);
  13.                                 var nameWidth:Number = p.firstName.length*atomRect.width;
  14.                                
  15.                                 var globalAtomRect:Rectangle = new Rectangle(rect.x+(atomRect.x-atomRect.width),rect.y,nameWidth,rect.height);
  16.                                
  17.                                 var box:Shape = new Shape();
  18.                                 box.graphics.beginFill(0xff0000,0.3);
  19.                                 box.graphics.drawRect(globalAtomRect.x,globalAtomRect.y,globalAtomRect.width,globalAtomRect.height);
  20.                                 containerSprite.addChild(box);
  21.                                
  22.                                 var localPosition:Point = new Point();
  23.                                 localPosition.x = containerSprite.x+rect.x+globalAtomRect.x;
  24.                                 localPosition.y = containerSprite.y+rect.y;
  25.                                
  26.                                 /*
  27.                                         We're zooming in so scale the local position
  28.                                 */
  29.                                 var scaledX:Number = localPosition.x*Config.SHIRT_ZOOM_SCALE;
  30.                                 var scaledY:Number = localPosition.y*Config.SHIRT_ZOOM_SCALE;
  31.                                
  32.                                 /*
  33.                                         We don't want the shirt to move so it is out of the viewport so calculate what this range is ...
  34.                                 */
  35.                                
  36.                                 var maxMoveX:Number = (Config.SHIRT_WIDTH*Config.SHIRT_ZOOM_SCALE)-Config.VIEWPORT_WIDTH;
  37.                                 var maxMoveY:Number = (Config.SHIRT_HEIGHT*Config.SHIRT_ZOOM_SCALE)-Config.VIEWPORT_HEIGHT;
  38.                                
  39.                                 var newX:Number = 0-scaledX;
  40.                                 var newY:Number = 0-scaledY;
  41.                                
  42.                                 var centeredX:Number = newX+Config.VIEWPORT_CENTER_X;
  43.                                 var centeredY:Number = newY+Config.VIEWPORT_CENTER_Y;
  44.                                
  45.                                 /*
  46.                                         Check the shirt hasn't moved out of bounds
  47.                                 */
  48.                                
  49.                                 var offsetX:Number = centeredX>0? centeredX*-1 : centeredX<0-maxMoveX? (centeredX*-1)-maxMoveX : 0;
  50.                                 var offsetY:Number = centeredY>0? centeredY*-1 : centeredY<0-maxMoveY? (centeredY*-1)-maxMoveY : 0;
  51.                                
  52.                                 /*
  53.                                         Finally, the new position is nudged so it is at the centre of the viewport space or as near as can be.
  54.                                 */
  55.                                 moveToPointX = centeredX>0? 0 : centeredX<0-maxMoveX? centeredX+((centeredX*-1)-maxMoveX) : centeredX;
  56.                                 moveToPointY = centeredY>0? 0 : centeredY<0-maxMoveY? centeredY+((centeredY*-1)-maxMoveY) : centeredY;
  57.                                
  58.                                 var viewportX:Number = Config.VIEWPORT_CENTER_X+offsetX;
  59.                                 var viewportY:Number = Config.VIEWPORT_CENTER_Y+offsetY;
  60.                                
  61.                                 shirtMoveTo.play();
  62.                                
  63.                                 var evt:PersonSelectionEvent = new PersonSelectionEvent(PersonSelectionEvent.PERSON_SELECTION,true);
  64.                                 evt.viewportPoint = new Point(viewportX,viewportY);
  65.                                 evt.person = p;
  66.                                
  67.                                 dispatchEvent(evt);
  68.                         }

Snippety Snipplr snip!

Actionscript No Comments »

So I’ve been using Snipplr recently for storing little code snippets. Looks like a pretty good service but after I tested its embedding features recently in a couple of posts I got sad and started to cry. This is because it didn’t embed very well. Not very well at all. You couldn’t scroll the code horizontally to read it.

Well, I’ve now installed the Snipplr wordpress plugin so hopefully the next bunch of stuff you see will be a couple of Snipplr snippets nicely embedded with some lovely scrolly-pollys scrollbars. Ok, I’ll stop talking like a baby now.

Get the server/domain name that your SWF is running on

  1. /*
  2. In Flex the Application needs to be complete so run this code within your applicationComplete event handler and pass a Boolean into a Data model somewhere so you can use it elsewhere.
  3. */
  4.  
  5. var Flex3Application:String = URLUtil.getServerName(Application.application.loaderInfo.url);
  6.  
  7. var Flex4Application:String = URLUtil.getServerName(FlexGlobals.topLevelApplication.loaderInfo.url);

Detecting if your SWF is running over HTTP or locally

  1. /*
  2. In Flex the Application needs to be complete so run this code within your applicationComplete event handler and pass a Boolean into a Data model somewhere so you can use it elsewhere.
  3. */
  4.  
  5. var isLocal:Boolean = loaderInfo.url.indexOf("file") == 0;

There’s a TextMate bundle too.

Exciting ay? The fun never stops here!

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in