Introduction to using the StatefulToolkit
Tutorial Overview
In this tutorial I’ll take you through the basic premise behind the StatefulToolkit and demonstrate how it can be used in a project. If you haven’t heard of StatefulToolkit before then be sure to read up on it. This example will also demonstrate use of the FileUtil, which is a utility class included in the toolkit that allows the user to load and save states. The application is very basic and will allow the user to position four circles and to save and load the state of the circles (i.e. where the circles are positioned). The example can be seen here and source files are available.
Preparation
Before you being implementing this tutorial make sure you have the latest version of the StatefulToolkit SWC, and that you’re project is set to target Flash Player 10 (if you want to target FP9, simple replace the Vector with an array in the circle container) . This tutorial also uses Keith Peter’s Minimal Comps component set to create the buttons in the application so you may want to grab the SWC for that too, but you can use your own buttons if you prefer.
Creating the Statable Object
We’re going to start by creating the circles that the user can move. In order for the position of the circles to be saved the class must implement the IStatable interface. The class itself is very simple; the constructor creates the graphics of the circle, the retrieveState method returns the position of the circle as a String, and the setState method sets the position of the Circle based on the new state. The retrieveState and setState methods are both required by the IStatable interface. Note that I’ve put the class in the couk.markstar.examples.ui package, but you don’t have to. The full contents of this class can be seen below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | package couk.markstar.examples.ui { import couk.markstar.statefultoolkit.core.IStatable; import flash.display.Sprite; public class Circle extends Sprite implements IStatable { /** * The constructor draws the circle with a grey outline */ public function Circle():void { super(); drawCircle( 0x999999 ); } /** * Change the display of the circle to show it's selected */ public function select():void { drawCircle( 0x990000 ); } /** * Change the display of the circle to the default */ public function deselect():void { drawCircle( 0x999999 ); } /** * Retrieve the state of the circle storing the x and y properties * * @return The current state of the circle as an XML string */ public function retrieveState():String { var xml:String = '<object>'; xml += '<x>' + x + '</x>'; xml += '<y>' + y + '</y>'; xml += '</object>'; return xml; } /** * Set the state of the circle by getting it's x and y values from the xml * * @param state The new state of the circle */ public function setState( state:XML ):void { x = Number( state.x ); y = Number( state.y ); } /** * Used internally for creating the circles' graphics * * @param lineColor The color of the line around the circle */ protected function drawCircle( lineColor:uint ):void { graphics.clear(); graphics.lineStyle( 1, lineColor ) graphics.beginFill( 0xFFFFFFF ); graphics.drawCircle( 0, 0, 10 ); graphics.endFill(); } } } |
Creating the Container
In this example the container is responsible for managing the circles. This includes creating and deleting circles, setting and retrieving the state of the circles and adding behaviours to the circles so the user can position them.
The circle container must also implement the IStatable interface ,so it can be used with the FileUtil. The container is set up to assume that an unknown amount of circles could be stored in the state, therefore there’s some additional code in the class to create and remove circles. Firstly, there’s the addObjectByState method, which takes the state of a circle as it’s parameter and creates a circle based on the state, and there’s the clearContainer method that loops through all of the circles and removes them from the stage.
The retrieveState and setState methods are also slightly different to the ones found in the Circle class. The retrieveState method loops through all circles and adding their states to the string to be returned, and the setState method clears any circles currently there and loops through the new state creating new circles as necessary. The last three methods of the class are event listeners that are there to enable the positioning of circles. The full contents of this class can be seen below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | package couk.markstar.examples.ui { import couk.markstar.statefultoolkit.core.IStatable; import couk.markstar.statefultoolkit.core.IStateSelectContainer; import flash.display.Sprite; import flash.events.MouseEvent; import flash.geom.Point; public class CircleContainer extends Sprite implements IStateSelectContainer { protected var _circles:Vector.<Circle>; protected var _currentCircle:Circle; protected var _dragOffset:Point; /** * The constructor the vector containing the circles shown in the container */ public function CircleContainer():void { _circles = new Vector.<Circle>(); super(); } /** * Retrieve the selected object * * @return The selected object */ public function retrieveSelectedObject():IStatable { return _currentCircle; } /** * Check whether the container has a selected object * * @return Boolean denoting if the container has a selected object */ public function hasSelectedObject():Boolean { return( _currentCircle ) ? true : false; } /** * Retrieve the state of the container and all contained circles * * @return The current state of the container, including the state's of all circles */ public function retrieveState():String { var xml:String = "<container>"; for( var i:uint = 0; i < _circles.length; i++ ) { xml += _circles[ i ].retrieveState(); } xml += "</container>"; return xml; } /** * Add an object to the container by it's state. * * @param state The state of the object to add, in this case the circle */ public function addObjectByState( state:XML ):void { var circle:Circle = new Circle(); circle.addEventListener( MouseEvent.MOUSE_DOWN, circleDownListener ); circle.setState( state ); addChild( circle ); _circles[ _circles.length ] = circle; } /** * Set the state of the container by adding all circles contained within the state. The container will be cleared first. * * @param state The new state of the container */ public function setState( state:XML ):void { clearContainer(); for( var i:uint = 0; i < state.object.length(); i++ ) { addObjectByState( XML( state.object[ i ] ) ); } } /** * Clear the container of all circles * */ protected function clearContainer():void { for( var i:uint = 0; i < _circles.length; i++ ) { removeChild( _circles[ i ] ); } _circles.splice( 0, _circles.length ); } /* * The following methods are all event listeners to enable the circles to be dragged */ protected function circleDownListener( e:MouseEvent ):void { // Deselect the current circle (if there is one) if( _currentCircle ) { _currentCircle.deselect(); } _currentCircle = Circle( e.currentTarget ); _currentCircle.select(); stage.addEventListener( MouseEvent.MOUSE_MOVE, circleMouseMoveListener ); stage.addEventListener( MouseEvent.MOUSE_UP, circleMouseUpListener ); _dragOffset = new Point( _currentCircle.x - mouseX, _currentCircle.y - mouseY ); } protected function circleMouseMoveListener( e:MouseEvent ):void { _currentCircle.x = mouseX + _dragOffset.x; _currentCircle.y = mouseY + _dragOffset.y; e.updateAfterEvent(); } protected function circleMouseUpListener( e:MouseEvent ):void { stage.removeEventListener( MouseEvent.MOUSE_MOVE, circleMouseMoveListener ); stage.removeEventListener( MouseEvent.MOUSE_UP, circleMouseUpListener ); } } } |
Creating the Document Class
The document class for this application is pretty simple. It’s responsible for creating the save and load buttons, creating the circle container, registering the circle container with the FileUtil and initialising the container with a default state. The save and load buttons are created using the Minimal Comps component set, you can replace these with buttons of your own if you choose not to use them. The full contents of this class can be seen below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | package { import com.bit101.components.PushButton; import couk.markstar.examples.ui.CircleContainer; import couk.markstar.statefultoolkit.utils.FileUtil; import couk.markstar.statefultoolkit.utils.IFileUtil; import flash.display.Sprite; import flash.events.MouseEvent; [SWF( backgroundColor='#F3F3F3',frameRate='30',width='592',height='300' )] public class SaveLoadExample extends Sprite { protected var _saveButton:PushButton; protected var _loadButton:PushButton; protected var _circleContainer:CircleContainer; protected var _fileUtil:IFileUtil; /** * Create the save and load buttons to save and load the state. Then create the circle container and register it with the FileUtil, * by registering the container with the FileUtil it means the FileUtil knows which statable object to retrieve and set the state on * when save and load are executed. */ public function SaveLoadExample():void { // PushButton( parent, x, y, label, MouseEvent.CLICK event listener ) _saveButton = new PushButton( this, 196, 275, "Save", saveClickListener ); _loadButton = new PushButton( this, 295, 275, "Load", loadClickListener ); _circleContainer = new CircleContainer(); _fileUtil = new FileUtil(); _fileUtil.registerObject( _circleContainer ); addChild( _circleContainer ); initDefaultState(); } protected function saveClickListener( e:MouseEvent ):void { _fileUtil.save(); } protected function loadClickListener( e:MouseEvent ):void { _fileUtil.load(); } /** * Initialise the circle container with four circles */ protected function initDefaultState():void { var defaultState:String = '<container>'; defaultState += '<object><x>196</x><y>100</y></object>'; defaultState += '<object><x>196</x><y>200</y></object>'; defaultState += '<object><x>396</x><y>100</y></object>'; defaultState += '<object><x>396</x><y>200</y></object>'; defaultState += '</container>'; _circleContainer.setState( new XML( defaultState ) ); } } } |
Testing the application
You will now be able to run the application and test that the state of the application can be saved and loaded. The structure of the project can be seen below. If you do have any questions about the tutorial, or constructive criticism on the toolkit, don’t hesitate leave a comment below.
Compiled Application
Once you have compiled the application, you should get the same output as below. Try moving the circles, saving the state then moving the circles again and loading the state. Notice that the circles go back to the saved state when it’s loaded.
Source Files
Source files are available from GitHub, under the heading SaveLoadExample.


[...] Update: Don’t forget to take a look at the Introduction to using StatefulToolkit. [...]
January 24th, 2010Looking good, might have to give this a shot with Box2D objects and see how it works.
January 25th, 2010@James That sounds brilliant. Keep me posted of any developments – it would be cool to see StatefulToolkit being used in a project (other than my own)!
January 25th, 2010[...] tutorial will build upon the Introduction to using the StatefulToolkit example; it will add the ability to utilize the Clipboard in our example. The example will use the [...]
February 16th, 2010