The Space For App Developers

Beyond Plain Old HTML Objects

Archive for the ‘Examples’ Category

as3viewnavigator – ViewNavigator for as3/flash projects

with 43 comments

I’m currently working on an application for the BlackBerry PlayBook tablet. The API that comes with the PlayBook SDK is based on pure AS3. Of course I could use Flex Hero for the job but I wanted to try out the “native” stuff that it comes with. My impression so far is really positive and the only thing that I’ve found missing so far was the concept of Views and ViewNavigator that comes with Flex Hero for mobile devices.

That is why I took a bit of time today to create library heavily inspired by ViewNavigator from Flex Hero that I could use for my pure as3/flash projects. The library is available for download here. Also checkout the video below for the details on how to use it.

Just for the reference, to tween the view transitions I used Tweener library that is also used internally by PlayBook API.

Below is the snippet of code that implements the example from the video above, you can also download the project source from here.

package
{
	import com.riaspace.as3viewnavigator.ViewNavigator;
 
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.MouseEvent;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
 
	import qnx.ui.buttons.LabelButton;
	import qnx.ui.core.Container;
	import qnx.ui.core.ContainerAlign;
	import qnx.ui.core.ContainerFlow;
	import qnx.ui.core.SizeMode;
	import qnx.ui.core.Spacer;
	import qnx.ui.text.Label;
 
	[SWF(width="600", height="1024", backgroundColor="#FFFFFF", frameRate="30")]
	public class Main extends Sprite
	{
		private var navigator:ViewNavigator;
 
		private var viewNumber:int = 0;
 
		public function Main()
		{
			initializeUI();
		}
 
		private function initializeUI():void
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			// Creating instance of ViewNavigator
			navigator = new ViewNavigator(this);
			// Pushing first view to the navigator
			navigator.pushView(createView());
		}
 
		private function createView():Container
		{
			var view:Container = new Container;
			view.flow = ContainerFlow.VERTICAL;
			view.align = ContainerAlign.MID;
 
			// Incrementing view number
			viewNumber++;
 
			// 20% spacer from the top
			view.addChild(new Spacer(20));
 
			// Adding label with view number
			var numLabel:Label = new Label;
			numLabel.text = viewNumber.toString();
			var format:TextFormat = new TextFormat;
			format.size = 70;
			format.bold = true;
			format.align = TextFormatAlign.CENTER;
			numLabel.format = format;
			numLabel.size = 100;
			numLabel.sizeMode = SizeMode.BOTH;
			view.addChild(numLabel);
 
			// 15% spacer between label above and buttons
			view.addChild(new Spacer(15));
 
			// Adding "push view" button
			var btn:LabelButton = new LabelButton;
			btn.label = "push view";
			btn.addEventListener(MouseEvent.CLICK,
				function(event:MouseEvent):void
				{
					navigator.pushView(createView());
				});
			view.addChild(btn);
 
			// Adding "pop view" button
			btn = new LabelButton;
			btn.label = "pop view";
			btn.addEventListener(MouseEvent.CLICK,
				function(event:MouseEvent):void
				{
					navigator.popView();
				});
			view.addChild(btn);
 
			// Adding "pop all" button
			btn = new LabelButton;
			btn.label = "pop all";
			btn.addEventListener(MouseEvent.CLICK,
				function(event:MouseEvent):void
				{
					navigator.popAll();
				});
			view.addChild(btn);
 
			// Adding "pop to first button" button
			btn = new LabelButton;
			btn.label = "pop to first";
			btn.addEventListener(MouseEvent.CLICK,
				function(event:MouseEvent):void
				{
					navigator.popToFirstView();
				});
			view.addChild(btn);
 
			// Adding "replace" button
			btn = new LabelButton;
			btn.label = "replace";
			btn.addEventListener(MouseEvent.CLICK,
				function(event:MouseEvent):void
				{
					navigator.replaceView(createView());
				});
			view.addChild(btn);
 
			return view;
		}
	}
}

Written by Piotr Walczyszyn

February 2nd, 2011 at 9:11 pm

Paged list loaded from sqlite in Adobe AIR

with 2 comments

During my recent work on the Adobe Evangelists Blogroll application I wanted to implement a lazy-loaded/paginated List component with data coming from a local SQLite database. The reason for this was, of course, a memory usage consideration. Adobe Evangelists Blogroll is a mobile application so I didn’t want to load all available Post objects for each selected blog at once. I wanted it to be loaded dynamically as the user scrolls through the list. It turned out this wasn’t really difficult but there were few gotchas that I wanted to share:

  1. You have to implement your own IList component that throws ItemPendingError whenever the item requested with getItemAt function is not available yet. Unfortunately the Flex SDK doesn’t come with one built-in, but the good news is that you can use PagedArrayList class I created; its source code is available here :)
  2. Implement a createPendingItemFunction for the AsyncListView component. This function gets called when ItemPendingError is thrown and in our case serves two purposes. One is to trigger fetching the next page of rows from the database. It also returns a String with text that will be displayed temporarily in missing rows of the List. When that data gets fetched from the database those missing rows will be replaced with the loaded ones.
  3. One gotcha for the previous point is that if you are using SQLConnection in synchronous mode you will have to start fetching rows in the next frame after the call to the createPendingItemFunction function. That is why in my example I’m using callLater to execute the fetchRows function. This problem doesn’t arise with an asynchronous database connection.
  4. The last gotcha is most likely a bug in the Flex SDK. It occurs when the AsyncListView.list property is set before its list object is initialized and its length value is set. In that case invalidIndex error is thrown inside of LinearLayoutVector. The workaround I came up with is that right after setting the length property, PagedArrayList dispatches FlexEvent.INITIALIZE event; the application logic should handle it and programmatically set the AsyncListView.list property to the PagedArrayList instance. For reference I filed a bug in Flex SDK Jira.

Below you can find the source code that resolves the gotchas above. Also the whole Flash Builder project with paged list implementation is available here.

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
					   xmlns:s="library://ns.adobe.com/flex/spark" 
					   xmlns:mx="library://ns.adobe.com/flex/mx"
					   xmlns:helpers="com.riaspace.helpers.*"
					   preinitialize="windowedApplication_preinitializeHandler(event)"
					   creationComplete="windowedApplication_creationCompleteHandler(event)">
 
	<fx:Script>
		<![CDATA[
			import mx.collections.errors.ItemPendingError;
			import mx.events.FlexEvent;
 
			protected var conn:SQLConnection;
 
			protected const PAGE_SIZE:int = 50;
 
			protected function windowedApplication_preinitializeHandler(event:FlexEvent):void
			{
				// Creating inmemory database
				conn = new SQLConnection;
				conn.open();
 
				// Creating data_tab table
				var createStmt:SQLStatement = new SQLStatement;
				createStmt.sqlConnection = conn;
				createStmt.text = "CREATE TABLE data_tab (value_col TEXT)";
				createStmt.execute();
 
				// Inserting 1000 records to the table
				var insertStmt:SQLStatement = new SQLStatement;
				insertStmt.sqlConnection = conn;
				insertStmt.text = "INSERT INTO data_tab VALUES (?)";
 
				for (var i:int = 1; i <= 1000; i++)
				{
					insertStmt.clearParameters();
					insertStmt.parameters[0] = "value " + i;
					insertStmt.execute();
				}
			}
 
			protected function windowedApplication_creationCompleteHandler(event:FlexEvent):void
			{
				// STEP 1 - query rows count
				var stmt:SQLStatement = new SQLStatement;
				stmt.sqlConnection = conn;
				stmt.text = "SELECT count(*) as rowsCount FROM data_tab";
				stmt.execute();
 
				var result:SQLResult = stmt.getResult();
 
				// STEP 2 - set PagedArrayList.length equal queried rows count 
				pagedArrayList.length = result.data[0].rowsCount;
 
				// STEP 3 - fetch actual data starting from 0 offset (1st row)
				fetchRows(0);
			}
 
			protected function fetchRows(offset:int):void
			{
				// Fetch data rows with specified limit which is our requested page size
				// and offset passed as parameter
				var stmt:SQLStatement = new SQLStatement;
				stmt.sqlConnection = conn;
				stmt.text = "SELECT * FROM data_tab LIMIT :limit OFFSET :offset";
				stmt.parameters[":limit"] = PAGE_SIZE;
				stmt.parameters[":offset"] = offset;
				stmt.execute();
 
				var result:SQLResult = stmt.getResult();
				if (result && result.data)
				{
					for(var i:int = 0; i < result.data.length; i++)
					{
						// Setting return row at offset + i position
						pagedArrayList.setItemAt(result.data[i], offset + i);
					}
				}
			}
 
			private function createPendingItemFunctionHandler(index:int, ipe:ItemPendingError):Object
			{
				// In case synchronous database mode is used fetchRows function should be called
				// after return from this function. With asynchronous mode fetchRows function can
				// be called directly.
				callLater(fetchRows, [index]);
 
				// Returning a message to display
				return "List items are being fetched from database...";
			}
 
			protected function pagedArrayList_initializeHandler(event:FlexEvent):void
			{
				// NOTICE: this is a workaround for a Flex bug that causes invalidIndex error
				// in LinearLayoutVector. It all works well when asyncListView.list property
				// is set after pagedArrayList is initialized and its length property is set.
				asyncListView.list = pagedArrayList;
			}
 
		]]>
	</fx:Script>
 
	<fx:Declarations>
		<helpers:PagedArrayList id="pagedArrayList" initialize="pagedArrayList_initializeHandler(event)" />
	</fx:Declarations>
 
	<s:List width="100%" height="100%" labelField="value_col">
		<s:AsyncListView id="asyncListView" createPendingItemFunction="createPendingItemFunctionHandler" />
	</s:List>
 
</s:WindowedApplication>

Written by Piotr Walczyszyn

December 24th, 2010 at 11:21 am

Posted in Examples

Tagged with , ,

Flash StageVideo API performance overview

with 8 comments

As you may know, today Adobe released a beta version of upcoming Flash Player 10.2. This release brings few really cool features especially in the field of Flash video. In the recording below I demonstrate really incredible performance gains that you can have with new StageVideo API, which gives you full GPU acceleration of video content.

Written by Piotr Walczyszyn

December 1st, 2010 at 4:49 pm

Posted in Examples

Tagged with ,

Video with an un-framework overview

with one comment

I wanted to share a video recording with an un-framework overview. This simple library has evolved a little bit from my previous post but I expect it will stay in this form for good ;) I don’t think it can evolve to anything more sophisticated but I hope that it will serve as an inspiration to others when building their own sets of helpers. I plan to use it solely as a substitue for my favorite DI frameworks when doing mobile Flex development.

Written by Piotr Walczyszyn

November 19th, 2010 at 4:36 pm

Posted in Articles,Examples

Tagged with , ,

UnFramework – or how to work without any frameworks

with 8 comments

Those who know me or at least subscribe to my blog know that I’m a big fan of Flex/AS3 frameworks. At this year’s Adobe MAX conference I even had my own session where I covered five major DI frameworks. Usually these frameworks provide a lot of productivity features and helpers like: IoC/DI, Event/Messaging bus, implementation of some architectural or design patterns (MVC, Command…), and many others. But what happens if we can’t or don’t want to use any of these frameworks?

Read the rest of this entry »

Written by Piotr Walczyszyn

November 16th, 2010 at 4:45 pm

Posted in Articles,Examples,Releases

Tagged with , ,

Extending model objects with ObjectProxy class

with 5 comments

Recently I was looking for an easy way to extend my existing model objects with additional properties. The problem I had was that there was no option to mark these model classes as dynamic. What I came up with was a solution that uses the ObjectProxy class that comes with Flex. ObjectProxy essentially is a class that allows you to track property changes of its wrapped object. So what I also got with this approach was an ability to register a PropertyChangeEvent handler with my model objects.

The snippet below shows how I extend my User class object with an additional selected property. This is a view specific property so it wouldn’t really be a good practice to do it directly in the model.

	// Wrapping each user into ObjectProxy with selected additional property selected 
	var userProxy:ObjectProxy = new ObjectProxy({user:user, selected:false});
	// Registering event PROPERTY_CHANGE handle
	userProxy.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, 
		userProxy_propertyChangeHandler);

Below is a reason why I needed all that and how it works in practice; you can right-click the example to view the source code. BTW: If you are looking for a simple and quick way to have a dynamic Checkbox List this a way to go.


This movie requires Flash Player 10

Another thing that the ObjectProxy class can resolve is a very common warning that occurs when you try to bind to not bindable properties: warning: unable to bind to property 'foo' on class 'Object' (class is not an IEventDispatcher).

Written by Piotr Walczyszyn

November 8th, 2010 at 4:20 pm

Posted in Examples

Tagged with

Slides and source code of my Adobe MAX 2010 presentation

with 5 comments

Adobe MAX 2010 is over and I’m heading back to Poland. Before I get on the plane I wanted to post my presentation deck and the source code of a demo app that I used to cover the DI frameworks. I also hope to post the session recording as soon as it gets to Adobe TV. BTW this years MAX was really awesome!

Written by Piotr Walczyszyn

October 28th, 2010 at 8:43 pm

Posted in Events,Examples,News

Tagged with , ,

Building NativeApplicationUpdater custom UI

with 17 comments

After I published the NativeApplicationUpdater library I received a lot of questions how to display a UI showing download and install progress. Unfortunately NAU doesn’t come with a built-in UI yet ;) So I decided to put together this short blog post to explain how you can build it yourself. Below you can see how the demo app with the custom UI looks (project source code is available here in SVN):




Below is snippet of code that you can examine to better understand how to build your own updater UI:

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
					   xmlns:s="library://ns.adobe.com/flex/spark" 
					   xmlns:mx="library://ns.adobe.com/flex/mx"
					   xmlns:updater="http://updater.riaspace.com/">
 
	<fx:Script>
		<![CDATA[
			import air.update.events.DownloadErrorEvent;
			import air.update.events.StatusUpdateEvent;
			import air.update.events.UpdateEvent;
 
			import mx.controls.Alert;
 
			[Bindable]
			protected var downlaoding:Boolean = false;
 
			protected function isNewerFunction(currentVersion:String, updateVersion:String):Boolean
			{
				// Example of custom isNewerFunction function, it can be omitted if one doesn't want
				// to implement it's own version comparison logic. Be default it does simple string
				// comparison.
				return true;
			}
 
			protected function updater_errorHandler(event:ErrorEvent):void
			{
				Alert.show(event.text);
			}
 
			protected function btnCheckNow_clickHandler(event:MouseEvent):void
			{
				// First initialize NativeApplicationUpdater
				updater.initialize();
			}
 
			protected function updater_initializedHandler(event:UpdateEvent):void
			{
				// When NativeApplicationUpdater is initialized you can call checkNow function
				updater.checkNow();
			}
 
			protected function updater_updateStatusHandler(event:StatusUpdateEvent):void
			{
				if (event.available)
				{
					// In case update is available prevent default behavior of checkNow() function 
					// and switch to the view that gives the user ability to decide if he wants to
					// install new version of the application.
					event.preventDefault();
					currentState = "updaterView";
				}
				else
				{
					Alert.show("Your application is up to date!");
				}
			}
 
			protected function btnYes_clickHandler(event:MouseEvent):void
			{
 
				// In case user wants to download and install update display download progress bar
				// and invoke downloadUpdate() function.
				downlaoding = true;
				updater.addEventListener(DownloadErrorEvent.DOWNLOAD_ERROR, updater_downloadErrorHandler);
				updater.addEventListener(UpdateEvent.DOWNLOAD_COMPLETE, updater_downloadCompleteHandler);
				updater.downloadUpdate();
			}
 
			protected function btnNo_clickHandler(event:MouseEvent):void
			{
				currentState = "mainView";
			}
 
			private function updater_downloadCompleteHandler(event:UpdateEvent):void
			{
				// When update is downloaded install it.
				updater.installUpdate();
			}
 
			private function updater_downloadErrorHandler(event:DownloadErrorEvent):void
			{
				Alert.show("Error downloading update file, try again later.");
			}
 
		]]>
	</fx:Script>
 
	<s:states>
		<s:State name="mainView"/>
		<s:State name="updaterView"/>
	</s:states>
 
	<fx:Declarations>
		<updater:NativeApplicationUpdater id="updater" 
					updateURL="http://riaspace.com/native-application-updater/update-1.1.xml" 
					isNewerVersionFunction="{isNewerFunction}"
 
					initialized="updater_initializedHandler(event)"
					updateStatus="updater_updateStatusHandler(event)"
 
					error="updater_errorHandler(event)"
					downloadError="updater_errorHandler(event)"
					updateError="updater_errorHandler(event)"
					/>
	</fx:Declarations>
 
	<s:Button id="btnCheckNow" label="Check for updates" includeIn="mainView" 
			  horizontalCenter="0" verticalCenter="0" 
			  click="btnCheckNow_clickHandler(event)"/>
 
	<s:HGroup verticalCenter="0" includeIn="updaterView" horizontalCenter="0" verticalAlign="top">
		<s:BitmapImage source="@Embed(source='/assets/icon128.png')" />
		<s:VGroup width="100%" horizontalAlign="center">
			<s:Label text="New version ({updater.updateVersion}) is available." />
			<s:Label text="Do you want to download it and install?" />
			<mx:ProgressBar id="prgBar" source="{updater}" label="Downloading %3%" visible="{downlaoding}" />
			<s:HGroup>
				<s:Button id="btnYes" label="Yes" click="btnYes_clickHandler(event)" />
				<s:Button id="btnNo" label="No" click="btnNo_clickHandler(event)" />
			</s:HGroup>
		</s:VGroup>
	</s:HGroup>
 
</s:WindowedApplication>

Written by Piotr Walczyszyn

October 15th, 2010 at 3:04 pm

Posted in Examples,Releases

Tagged with ,

Swiz 1.0 Command pattern API explained

with 9 comments

As you may know the Swiz team has released RC2 of the upcoming 1.0 version. Among many improvements and bugfixes there is one new feature that brings a built-in command pattern to Swiz. As part of my prep work for Adobe MAX where I will be speaking about AS3/Flex Frameworks I wanted to investigate it in more depth. As you can read in the Swiz docs, this implementation borrows heavily from the RobotLegs framework, which I will be also covering together with Parsley. This approach is really clean and simple but to be honest I kinda hope that it will evolve in future releases to be more metadata-driven, which is actually what Swiz is all about ;)

In the example below you can see it in action (right-click to view the full source code).


This movie requires Flash Player 10

So how do we get started?

If, when you read this post, Swiz is still in RC2, then you will need to grab the latest source code from github and build it yourself, the released RC2 has some quirks in regards to its command pattern implementation.

The first thing you do is create a SaveUserCommand class that implements the IEventAwareCommand interface and has one execute function. Obviously this is the function that will implement the command logic. It doesn’t accept any parameters, so if you need any you should provide them by injection. UPDATE 23.09.2010: Triggering event is available through event setter function when you implement IEventAwareCommand instead of ICommand.

package com.riaspace.max.swiz.commands
{
	import com.riaspace.max.swiz.events.UserEvent;
 
	import flash.events.Event;
 
	import mx.controls.Alert;
 
	import org.swizframework.utils.commands.IEventAwareCommand;
 
	public class SaveUserCommand implements IEventAwareCommand
	{
 
		private var _event:UserEvent;
 
		public function set event(value:Event):void
		{
			_event = value as UserEvent;
		}
 
		public function execute():void
		{
			Alert.show("SaveUserCommand executed, userName: " + _event.userName);
		}
	}
}

Next you can map/configure our command using a MyCommands class that extends the Swiz specific CommandMap. Just a hint here for the Swiz team that it would be cool to have the ability to configure commands directly in BeanProvider in future releases ;)

package com.riaspace.swiz.configs
{
	import com.riaspace.swiz.commands.SaveUserCommand;
	import com.riaspace.swiz.events.UserEvent;
 
	import org.swizframework.utils.commands.CommandMap;
 
	public class MyCommands extends CommandMap
	{
		override protected function mapCommands():void
		{
			// Mapping UserEvent.SAVE to SaveUserCommand class
			mapCommand(UserEvent.SAVE, SaveUserCommand, UserEvent, false);
		}
	}
}

Now you can instantiate the MyCommands class in the BeanProvider like any other bean:

<?xml version="1.0" encoding="utf-8"?>
<swiz:BeanProvider
	xmlns:fx="http://ns.adobe.com/mxml/2009"
	xmlns:swiz="http://swiz.swizframework.org" xmlns:commands="org.swizframework.utils.commands.*" xmlns:configs="com.riaspace.swiz.configs.*">
	<fx:Script>
		<![CDATA[
			import com.riaspace.swiz.models.presentation.MainPM;
		]]>
	</fx:Script>
 
	<fx:Declarations>
 
		<configs:MyCommands />
 
		<swiz:Prototype type="{MainPM}" />
 
	</fx:Declarations>
 
</swiz:BeanProvider>

At last you can dispatch the UserEvent.SAVE event in your PM class and the SaveUserCommand.execute will be called.

package com.riaspace.swiz.models.presentation
{
	import com.riaspace.swiz.events.UserEvent;
 
	import flash.events.IEventDispatcher;
 
	public class MainPM
	{
		[Dispatcher]
		public var dispatcher:IEventDispatcher;
 
		public function btnExecuteCommand_clickHandler():void
		{
			dispatcher.dispatchEvent(new UserEvent(UserEvent.SAVE, "John"));
		}
	}
}

Written by Piotr Walczyszyn

September 20th, 2010 at 6:41 pm

AS3TextArea improved

with one comment

I wasn’t really happy with the performance of my AS3 syntax coloring component AS3TextArea. That is why I took some time to investigate TLF (Text Layout Framework) further and I managed to gain some serious improvements. From over 1000 ms to do the text coloring it was reduced to around 490 ms (as a test case I used AS3TextArea component source code which is 334 lines long). I know its not perfect yet especially with single-threaded Flash Player but it is actually usable now with larger AS3 files ;)

The whole secret is that I’m no longer reimporting the whole TextFlow over and over again. Now I’m only reformatting it on every change. Another important factor here is that the reformatting is performed in a batch using the ApplyFormatOperation and CompositeOperation classes. Checkout the snippet below, which does the magic. You can also play with improved AS3TextArea here:

	// Creating new CompositeOperation
	var compositeOperation:CompositeOperation = new CompositeOperation();
 
	// Reseting whole text to the default TextLayoutFormat
	var operationState:SelectionState = new SelectionState(textFlow, 0, text.length);
	var formatOperation:ApplyFormatOperation =
		new ApplyFormatOperation(operationState, formats.text, null);
	compositeOperation.addOperation(formatOperation);
 
	// Executing RegExp for the first token
	var token:* = syntax.exec(this.text);
	while(token)
	{
		// Getting token value
		var tokenValue:String = token[0];
		// Detecting token type
		var tokenType:String = getTokenType(tokenValue);
		// Getting TextLayoutFormat for current token type
		var format:TextLayoutFormat = formats[tokenType]; 
 
		// Creating new SelectionState for at the location of current token
		operationState = new SelectionState(textFlow,
			token.index, token.index + tokenValue.length);
 
		// Creating new ApplyFormatOperation for current token
		formatOperation = new ApplyFormatOperation(operationState,
			format, null);
 
		// Adding ApplyFormatOperation to CompositeOperation
		compositeOperation.addOperation(formatOperation);
 
		// Incrementing RegExp syntax lastIndex after the current token
		syntax.lastIndex = token.index + tokenValue.length;
 
		// Executing RegExp for the next token
		token = syntax.exec(this.text);
	}
 
	// Executing batch of ApplyFormatOperation's
	compositeOperation.doOperation();

So if you need to format particular portions of your TLF-based text I believe this is the best way to go.

Written by Piotr Walczyszyn

September 10th, 2010 at 1:47 pm

Posted in Examples

Tagged with