AIR for Android: Phoenix Traffic Released in Android Market


Phoenix Traffic for Android

I finally released my first application into the Android Market. This application takes data for Phoenix traffic cameras and displays live camera images. That part was simple enough, but deciding to go with either Flex or Flash for development was the hard part. Originally, I built the application using Flex 4 (this was pre-Hero and Burrito) and talked about it in a previous post. However, I found the Flex application to be memory intensive, lethargic, and the scrolling list functionality simply unusable.

For the final release of the application, I decided to use Flash CS5. This allowed me to build a somewhat smaller version of the application that could be easily be deployed using Adobe’s AIR technology for Android directly from Flash. As part of this project I built (or rather modified code from others) an AS3 scrolling list that responds to touch events and behaves similar to native Android scrolling lists.

In my opinion, the list still performs better than some of the current controls in being built for Flex Burrito. This might change in the near future, but for now, AS3 only applications on Android seem to be lighter and perform better than Flex. If you are interested, you can see the code and example files for the scrolling list here in this post.

To download the Phoenix Traffic application, you can visit the Phoenix Traffic application page from this site or just search for “Phoenix Traffic” in the Android Market. Please feel free to leave me any comments or feedback about the application. I will continue to tinker with the application and improve it over time.

Update

This application has since been migrated from AIR to a native Android application.

- Mister

AS3 Scrolling List for Android and iOS devices

I created a very simple AS3 list that works with the both Android and iOS devices. The project files include a Flash Professional project created with Flash Builder 4. You will need Adobe AIR for Android or the packager for iPhone to create naive create native iOS (iPhone, iPad) or Android.

If you only want to build for Android, then I recommend you check out Adobe Flash Builder Burrito and the Flex Hero SDK (or SDK 4.5). There is already a list control in the Flex Hero SDK (or SDK 4.5) for Android devices. However for iOS devices, you will need a scrolling list that works with AS3 and CS5 for packaging.

The list I created is an AS3 list that works for multiple devices, touch scrolls, and uses custom item renderers that detect user interaction. Here is an example AS3 project with the list in action. Just use your mouse like you would your finger on a mobile device to scroll the list and select items.



AS Scrolling List (click to view)

The list is suited best for smaller sets of data because the list does not recycle list items. But for most mobile applications you don’t normally have that many items to scroll. Adobe also recommends that you not use the drawing API in Flash because of its memory consumption on mobile devices. It would be better to create a MovieClip or Sprite in Flash and use that as the background of your item renderer. However, int this project I used the drawing API to change the selection color of the list item throwing all caution to the wind.

TouchList

The TouchList class creates the list, adds items and handles touch events dispatched by the item renderers. You might notice that I didn’t use any actual TouchEvent listeners in the list. This is because a TouchEvent is essentially a MouseEvent and I couldn’t see any difference in using one over the other. The TouchList class has a built in delay to differentiate between a scrolling and touch action. Like the Android phone, you can’t select an item while scrolling and pressed items are deselected if you scroll while pressed.

TouchListItemRenderer

TouchListItemRenderer implements ITouchListItemRenderer and renders the display of the items in the list data. This renderer can be customized to show whatever type of data you want in the list. List items can also be variable height.

ITouchListItemRenderer

If you want to create an item renderer for the list, then it must implement the ITouchListItemRenderer interface. This interface gives the renderer basic functionality to interact with user selection and touch events used by the list.

ListItemEvent

The list item event is a custom custom ListItemEvent dispached when a list item renderer is pressed. The event contains the event payload and a instance of the item renderer selected.

Installation

Included in the GitHub repository is the working project files for that I created in Flash Builder 4 that handles adding the list to the stage, screen orientation on the device, stage resize, and other functions for an Android AIR application. To install, just checkout the project file and import it into Flash Builder. I have also included Android .apk file if you want to deploy it directly to your Android phone.

The AS3ScrollinList project is located on GitHub.

If you do use the list in a project, be sure to drop me a note or mention me in your will. This list is actually a combination of my efforts and those of others in the Flash community. So please share what you build as well. If you have improvements, just post them back to this post or feel free to fork the GitHub code.

Work Cited

-Mister

Simple AS3 Mouse Scrolling List

Here is a simple ActionScript 3 list that scrolls with selectable items done two ways. The list itself acts like a component added to the stage, it can be sized and placed anywhere on the stage. The first way scrolling is achieved is based on the movement of the mouse over a defined masked area.

The smooth movement of the list is accomplished with TweenLite:

ListScroll.swf

The second way to scroll the list was on an enter frame event. This method is not as smooth because TweenLite could not be produced to create the movement of the list:

ListScroll2.swf

That’s pretty much the extent of it. Below is the code for the list application and scrolling list.

List.as

package
{
    import flash.display.MovieClip;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.text.TextField;

    [SWF( width = '320', height = '440', backgroundColor = '#000000', frameRate = '30')]
    public class ListScroll extends Sprite
    {
        private var list:MouseScrollList;

        public function ListScroll()
        {
            addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e:Event):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            list = new MouseScrollList(300, 400, false);
            list.y = 20;
            list.x = 5;
            this.addChild(list);
        }
    }
}

MouseScrollList.as

package
{
    import com.greensock.TweenLite;
    import com.greensock.easing.Quad;

    import flash.display.MovieClip;
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.text.TextField;

    public class MouseScrollList extends Sprite
    {
        private var background:Sprite;
        private var menuMask:Shape;
        private var listHitArea:Shape;
        private var list:Sprite;

        private var prevY:Number = 0;
        private var listHeight:Number;
        private var hitAreaHeight:Number;
        private var listY:Number;
        private var listX:Number;
        private var listInitY:Number;

        private var verticalPadding:Number = 5;
        private var itemHeight:Number = 35;
        private var children:Number = 50;

        private var componentWidth:Number;
        private var componentHeight:Number;

        private var scrollOnMouseMove:Boolean = false;  // uses mouse move instead of enter frame, but doesn't work as good as I thought
        private var scroll:Boolean = true; // on mouse down set to false to hold scroll position in list when item is clicked

        public function MouseScrollList(w:Number, h:Number, scrollOnMouseMove:Boolean = false)
        {
            this.componentWidth = w;
            this.componentHeight = h;
            this.scrollOnMouseMove = scrollOnMouseMove;

            addEventListener(Event.ADDED_TO_STAGE, init);
            addEventListener(Event.REMOVED, destroy);
        }

        private function init(e:Event):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);

            if(scrollOnMouseMove){
                addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
            } else {
                addEventListener(Event.ENTER_FRAME, handleEnterFrame);
            }

            listHitArea = new Shape

            menuMask = new Shape();
            menuMask.graphics.clear();
            menuMask.graphics.beginFill(0xFFFFFF,.4);
            menuMask.graphics.drawRect(0, 0, componentWidth, componentHeight)
            menuMask.graphics.endFill();

            this.addChild(menuMask);

            list = new Sprite();
            list.mask = menuMask;

            this.addChild(list);

            for(var i:int = 0; i < children; i++) {
                var textField:TextField = new TextField();
                textField.text = String(i);
                textField.mouseEnabled = false;

                var item:Sprite = new Sprite();
                    item.graphics.clear();
                    item.graphics.beginFill(0xFF0000, 1);
                    item.graphics.drawRect(0, 0, componentWidth - verticalPadding*2, itemHeight)
                    item.graphics.endFill()
                    item.y = i*(verticalPadding + itemHeight);
                    item.x = verticalPadding;

                    item.addEventListener(MouseEvent.ROLL_OVER, handleRollOver);
                    item.addEventListener(MouseEvent.ROLL_OUT, handleRollOut);
                    item.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
                    item.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp);

                    item.addChild(textField)
                list.addChild(item);
            }

            listHitArea = new Shape();
            listHitArea.graphics.clear();
            listHitArea.graphics.beginFill(0xFFFFFF,0);
            listHitArea.graphics.drawRect(0, 0, componentWidth, componentHeight)
            listHitArea.graphics.endFill();

            this.addChild(listHitArea);

            var point:Point = new Point(listHitArea.x, listHitArea.y);
                point = this.globalToLocal(point);

            listX = point.x;
            listY = point.y;
            listInitY = list.y;
            hitAreaHeight = listHitArea.height;
            listHeight = children*(verticalPadding + itemHeight) - hitAreaHeight;
        }

        private function scrollMouseMove(y:Number):void
        {
            var percent:Number = y/hitAreaHeight;
            var newY:Number = -(Math.round(listHeight*percent));

            if(newY + itemHeight*3 > hitAreaHeight) {
                newY = hitAreaHeight;
            } else if (newY + itemHeight*3 > listInitY) {
                newY = listInitY;
            }

            TweenLite.to(list, 2, {y:newY, ease:Quad.easeOut});
        }

        private function scrollEnterFrame(y:Number):void
        {
            var distance:Number = Math.cos( ( -(y + listY)/hitAreaHeight)*Math.PI )*15;
            var currentY:Number = list.y;
            var newY:Number;

            if( (currentY + distance - verticalPadding) > listInitY + verticalPadding) return;

            newY = list.y + distance;

            var delta:Number = Math.abs(prevY - newY);

            if(delta < 1) {
                list.y = prevY;
                return;
            }

            if(newY >= listInitY) {
                newY = listInitY;
            } else if (Math.abs(newY) > listHeight){
                newY = -listHeight;
            }

            prevY = list.y;
            list.y = newY;
        }

        private function handleMouseMove(event:MouseEvent):void
        {
            var x:Number = this.mouseX + Math.abs(listX);
            var y:Number = this.mouseY + Math.abs(listY);

            if( menuMask.hitTestPoint( x, y )  && scroll) {
                scrollMouseMove(y - Math.abs(listY));
            }
        }

        private function handleEnterFrame(event:Event):void
        {
            var x:Number = this.mouseX + Math.abs(listX);
            var y:Number = this.mouseY + Math.abs(listY);

            if( listHitArea.hitTestPoint( x, y ) && scroll  ) {
                scrollEnterFrame(y);
            }
        }

        public function handleRollOver(e:MouseEvent):void
        {
            e.target.alpha = .5;
        }

        private function handleRollOut(e:MouseEvent):void
        {
            e.target.alpha = 1;
        }

        private function handleMouseDown(e:MouseEvent):void
        {
            e.target.alpha = .2;
            scroll = false;
        }

        private function handleMouseUp(e:MouseEvent):void
        {
            e.target.alpha = 1;
            scroll = true;
        }

        private function destroy(e:Event):void
        {
            removeEventListener(Event.REMOVED, destroy);

            var list:MovieClip = list as MovieClip;

            if(list.numChildren != 0) {
                var i:int = list.numChildren;
                while( i-- ){
                    var item:Sprite = list.getChildAt(i) as Sprite;
                    item.removeEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
                    item.removeEventListener(MouseEvent.MOUSE_UP, handleMouseDown);
                    item.removeEventListener(MouseEvent.ROLL_OVER, handleRollOver);
                    item.removeEventListener(MouseEvent.ROLL_OUT, handleRollOut);
                    list.removeChildAt( i );
                }
            }

            removeEventListener(Event.ADDED_TO_STAGE, init);
            removeEventListener(Event.REMOVED, destroy);
            removeEventListener(Event.ENTER_FRAME, handleEnterFrame);
            removeEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
        }
    }
}

You can download a Flash working example here.

- Mister

Phoenix Traffic Android Mobile Application built with AIR

UPDATE 4/04/2012
The application has been migrated from Adobe AIR for Android to a native Android application. Still available in the market under the same name.

UPDATE
I recently released the Phoenix Traffic application on the Android market. The application was built using CS5 rather than Flex. I also created my own custom scrolling list that works in AS3 for iPhone and Android. You can find out more on this post:

Phoenix Traffic Released in Android Market

AS3 Scrolling List for Android and iOS

Original Post

I developed a simple Flex 4 application targeting AIR for Android. For this application, I used Flex to build a somewhat smaller version of a previous application that uses XML data to display a list Phoenix traffic cameras locations and images. The images are updated on an set interval and locations are selected from a list of freeways and intersections for each traffic camera.

Initially, I built the application in Flash, but ran into some issues and annoyances with creating a proper list that worked with the Multitouch events for capturing user gestures on multitouch devices like Android.

The Flex 4 list has support for TouchEvent. However, it’s still difficult to differentiate between the different TouchEvent’s being fired (TouchEvent.TOUCH_BEGIN, TouchEvent.TOUCH_MOVE, TouchEvent.TOUCH_END, TouchEvent.TAP). As a result, the list has a tendency to select items while scrolling the list.

Here are some screen shots of the application running on my NexusOne:

 

The Flex preloader was replaced with a simple splash screen graphic.

Simple list of freeways.

Once you select a freeway, you see list of camera locations for that freeway.

Selecting a camera takes displays the live camera image accompanied by two still images for direction.

This was my first run at doing Android and I can see some potential. Deploying AIR application from Flex 4 to your Android device is pretty easy, though nothing like the experience publishing from Flash CS5. The applications tend to be a little robust, hopefully this will be resolved with the release of the Halo components which are designed specifically for mobile devices.

To build your own AIR for Android applications, you need an Android device running Android 2.2 which supports Flash Player 10.1 and AIR. you also need to sign up for the AIR for Android Prerelease program. There are some good example applications with code starting to appear, including a scrolling list. Here are the posts I referenced used building this application:

Flex 4 List Scrolling on Android with Flash Player 10.1
“VoiceNotes for Android”: Sample App using Flex, AIR, and the Microphone API
AIR on Android: TweetrApp Video walk-through
Employee Directory Sample Application Using Flex and AIR for Android – Updated for Froyo
Flex 4 Application Handling Touch Events on Android with Flash Player 10.1

- Mister

ImageCache, a cheap way to cache images in Adobe Flex

In a previous post about Ely’s SuperImage, I mentioned that we decided to implement a simpler method for caching images within a Flex application. ImageCache is a simple class file that extends the Image control by adding the ability for the control to cache bitmap data. Unlike SuperImage, ImacheCache can handled SWF files, it just lacks all the bells and whistles of SuperImage, though image flicker is eliminated.

Using ImageCache

ImacheCache is just two class files. ImageCache.as which extends the Image control and ImageCachUtility.as which is a Singleton class that used by Imagecache to store and retrieve the BitmapData for cached images. Bitmap data is stored in a regular ArrayCollection object and retrieved by using the full path to the image (because image names can be duplicates but paths should be unique). Once the amount of cached images reaches the cache limit, the controls works on first in first out, dropping off older cached images as it caches new ones and staying within the cache limit. SWF fiels are not actually cached and will load as normal. You just use ImageCache like you would the Image control within your project:


<controls:ImageCache cacheLimit="200" id="image" source="{data.url}" width="100" height="100" complete="imageComplete()" ioError="imageError()" xmlns:controls="com.thanksmister.controls.*"/>

Cache Limit

ImageCache has a property called “cacheLimit” which tells the control how many images to cache. Because caching images can reduce the performance of your application. The larger the cache limit value, the more memory your application will use because it is storing all the Bitmap data in memory. If you want to reset the image cache, create an instance of the ImageCacheUtility and call the method “clear()”. ImageCache has been used and tested extensively for large media sites. Below is a screen shot of images from Flickr, the list on the left uses the standard Image control, the list on the right uses the ImageCache control. It seems that there may be some security issue when loading Flickr images without doing a proxying the images. Run the example locally or proxy Flickr images for best results.

Example

Code

You can download the code for the above project here. You will need your own Flickr API key to make the application work.

-Mister

Scrolling List Control with Tweener

I was on a simple little side task last week, to create a scrolling effect using the Flex control and also remove the default scroll bars. I didn’t want to do the usual trick, scrolling a HBox with items added to it using a repeater, or whatever. I wanted to animate the scrolling that occurs within an actual List control but give it a little easing effect. The call went out to Twitter for suggestions, and within minutes Jesse Warden responded with with “use the tween class dude” and also my friend Jerry Don reminded me of a new smooth scrolling List created by Alex Harui. This helped to point me in the right direction.

Scrollbars and Mouse Wheel

The first objective was to remove the default List scroll bar because I wanted to use buttons to scroll the list from the top to bottom and vice versa with just one click of a button. Ok, no problem, verticalScrollPolicy=”off” just hides the scroll bar, however it also removes the ability to scroll the list with the mouse wheel. Now I had to hack into the List control to add this functionality back. This part was not too hard, I found a good code example, though it was in German, and repurposed it for my needs.

 /**
 * Override the mouseWheelHandler so if the verticalScrollPolicy is "off"
 * we use the MouseEvent.delta value to manually update the List control
 * scroll position when there is no ScrollBar.
 *
 * */
 override protected function mouseWheelHandler(event:MouseEvent):void
 {
 if(verticalScrollPolicy == ScrollPolicy.OFF && maxVerticalScrollPosition != 0){
 var len:Number = dataProvider.length;
 var delta:Number = Number(event.delta);
 var pos:Number = verticalScrollPosition;
 var newpos:Number = verticalScrollPosition;

if(delta = maxVerticalScrollPosition) {
 newpos = maxVerticalScrollPosition;
 } else {
 newpos = pos + Math.abs(delta);
 }
 } else {
 if( ((pos - delta) <= 0 ) || pos == 0){
 newpos = 0;
 } else {
 newpos = pos - delta;
 }
 }

verticalScrollPosition = newpos;
 }

super.mouseWheelHandler(event);
 }
 

Tween List

I then through on some buttons that would scroll the list from the current position to either the start or the end. Now began the work with to add the easing function so the list would scroll fast at the start of the scroll and then slow down just before reaching the end or beginning of the list, depending on which way you want to scroll. I started with the built in mx.effects.Tween class which really proved to be the least effective. One problem with the List control is that its not incremental, it scrolls by row, making for a choppy scroll experience. So instead of being able to scroll in smaller increments, you have to scroll by whole numbers, which doesn’t allow enough runway to make the list easing effect noticeable. Using the regular Tween and a mx.effects.easing.Circle effect might look something like this:

 private function tweenDown():void
 {
 var pos:Number = list.verticalScrollPosition;
 var end:Number = list.maxVerticalScrollPosition;
 var tween:Tween = new Tween(this, pos, end, 1400, 10, tweenUpdateHandler, tweenEndHandler);
 tween.easingFunction = Circular.easeOut;
 }

private function tweenUpdateHandler(value:String):void
 {
 list.verticalScrollPosition = Math.round(Number(value));
 }

private function tweenEndHandler(value:String):void
 {
 trace('tween end value: ' + value);
 }
 

Notice that you need to round the value returned in the tweenUpdateFunction, if you don’t, then your list will get hosed because the list can’t handle values like

tween value: 0.03486542280255378
tween value: 0.01960467102681207
tween value: 0.01022360862317484
tween value: 0.0032527319110684516
tween value: 0.0001680006720050642

Smooth Scrolling List

This is where I thought Alex’s smooth scrolling list would come into play because it allows for or smooth scrolling by increments. In theory, you shouldn’t have to round up the number, you should be able to apply the values directly to the verticalScrollPosition property of the list. Alex’s prototype is suppose to allow this, which it does to some extent. The prototype list can handle it when you scroll down, but not back up to the top, it breaks down. So this doesn’t really solve the problem of smoothly scrolling the list when updating the verticalScrollPosition with a Tween effect. However, his list does have some value as it allows for smooth scrolling when you use the mouse wheel.

Example

Below is the example and code. Don’t expect the mouse wheel to work if you are on Mac OS X, because its not supported in the Flash Player. If you are interested in a work around for the mouse wheel issue, check out this the piexelbreaker.com post for a workaround. Of course the mouse wheel works fine in Adobe AIR. Also, the longer the list of data, the better the animation effect because there is more data to show the full effect. The effect could still be improved, but until Adobe releases a new set of List components that scroll properly, this is as good as it may get. Let me know if anyone has any other suggestions.

Download the ScrollTweenEffect.zip file for the source code.

UPDATE

A friend suggested I look at the Flex mx.effects.AnimateProperty to animate the scrollbar for the List. He pointed to me to a blog post by Thomas Decaux. I will try to work up an example using his approach to compare the two.

- Mister

SuperImage Redux, caching images in Flex List

I have been meaning for so long to post this code. Some time ago I had the pleasure of working with John Yanarella from Universal Mind. John was helping my employer at the time to put together an application that allows users to upload, manage, and share media assets. We needed an efficient way to cache and display images to optimize the performance of loading and viewing a large number of thumbnails.

SuperImage

Last year Ely Greenfield posted on his blog QuietlyScheming a way to end images from flickering when you display them in a Flex List control. Ely created a new component called SuperImage that replaces the Flex Image control. SuperImage fixes a few issues with the current Image control layout in addition to adding the ability to cache loaded images to stop them from constantly reloading (causing the flicker when scrolling a list).

SuperImage Update

In our project we wanted use SuperImage, but what we wanted was for SuperImage to behave more like the Flex Image control. Specifically we wanted the control to broadcast the same standard events as the Image control; ioError, securityError, imageComplete, progress, completem, completeEffect. The new SuperImage also implements IDataRenderer, IDropInListItemRenderer, and IListItemRenderer interfaces. John Yanarella did a great job cleaning up the SuperImage control and add the missing functionality. We ended up not using the SuperImage and instead used a simpler implementation for caching the Image control.

Example

Below is an example similar to Ely’s SuperImage that demonstrates the problem (on the left) with the Image control and the fix (on right) using the updated SuperImage. This example also shows a text dump of events broadcast from the new SuperImage control.

Limitations

One thing the updated SuperImage still lacks is the ability to display loaded SWF files. Since our company never used the code in any project and it was based on Ely’s code, I thought it only fair to repost the update and the code for everyone to use as needed. If anyone has an further updates to the code or suggestions to fix the SWF loading issue, please post them here and I will continue to update the component.

Code

You can download the code for the above project here. You will need your own Flickr API key to make the application work. Be sure to also read Ely’s original post to see the other benefits of SuperImage. Many thanks to John Yanarella who actually did the coding on the project, I finally posted it as I promised him many months ago :) .

-Mister

Flex List Control with Editable InputText ItemRenderer

I think the title of this post says it all.   I needed a InputText for the label of images in my TitleList control.  I wanted users to click on the text and edit it in place.  I started out creating a simple ItemRenderer with a InputText and skinned the InputText to look like a regular label until you click to edit the information.   I then decided to go a little further and make a custom control based on InputText which did all the style changes and kept broadcast a change event only when the text actually changes.   I also removed the extension from any titles (like myimage.jpg becomes myimage instead).

This was all fine and dandy until I used the component within the TileList.   You see, the List controls allow Alphanumeric keys as a form of navigation.  This is really cool because if you have images and titles below them, you can skip from image to image by typing the first letter of the image title.  However, this conflicted with the InputText.  Each time I clicked on a title to edit and started to type, the TitleList would navigate based on the character I typed.   This was not so cool. 

I ended up with a simple solution:

event.stopImmediatePropagation();

I used stopImmediatePropogation to prevent my keyDown events on the InputText from bubbling up to the List control.  Problem solved!  Here is a working example, right-click on the Flex application to get the code (I can’t tell you how many people post messages about how to get the Flex source code from examples, I mean, if you have to ask….).

 

ItemRendererEditor Example and Source

 

- Mister