Our thoughts in real time
AIR/FLex
August 08, 2010
The North Face Micro Sites Go Live
The North Face has recently released several micro-sites that celebrate top-notch athletes' accomplishments in their respective sports. Grio is proud to be part of the team that helped turning attractive and stunning graphical designs into live interactive sites. Check them out and enjoy!!!
http://www.thenorthface.com/en_US/brand/snowsports
http://www.thenorthface.com/en_US/brand/running
Posted at 11:32 PM in AIR/FLex, News | Permalink | Comments (0) | TrackBack (0)
July 19, 2010
Playing Video File from Limelight Secured URL
We worked on a project that utilized 360° panorama viewer, called PanoSalado, and ran into an issue with streaming video from Limelight, a popular CDN service.
PanoSalado renders one more more video sources by capturing the bitmaps and laying them "flat" allowing user to pan and zoom. For this project, our video source (encapsulated by a class called VideoSource) contains an instance of FLVPlayback. This is a standard Flash class for playing flv videos. To use it to play an flv file, one can simply call its load function.
var videoURL:String = "http://blabla.vo.llnwd.net/bla/bla/abc.flv";
_video.load(videoURL);
_video is an instance of FLVPlayback. And the core of FLVPlayback is a class called VideoPlayer.
Now, here's the issue. When we assign videoURL with Limelight secured url that has expiration time (ie., the flv is accessible for X minutes after it's first available), FLVPlayback stops working. For some reasons, it doesn't like the secured URL which has the following format:
http://blabla.vo.llnwd.net/bla/bla/cde.flv?e=...&h=...
The extra parameters after .flv are necessary for Limelight to recognize the validity of the request. Without them, cde.flv is not accessible.
To address this issue, we have to find out its root cause. When load function is called, FLVPlayback passes the call to VideoPlayer which then utilizes NCManager class to, among other things, validate the URL and determine its protocol (progressive vs. streaming/RTMP). What makes the request to secured URL break is the fact that NCManager only considers files with ".flv" postfix a valid non-streaming file. This check occurs in connectToURL function inside NSManager.as:
public function connectToURL(url:String):Boolean {
...
if (... parseResults.streamName.slice(-4).toLowerCase() == ".flv" ) {...
Okay, now that we know the cause, it's fairly straightforward to come up with the fix. What we need is a custom NCManager which will allow the limelight secured url format. We created a class call LimelightNCManager and it looks like this:
public class LimelightNCManager extends NCManager
{
public var LIMELIGHT_ID:String = "llnwd.net";
override public function connectToURL(url:String):Boolean {
var ret:Boolean;
// if this is limelight url, we assume it's an http...
if (url.indexOf(LIMELIGHT_ID)>0 &&
url.indexOf("http://")>=0 &&
url.indexOf("?")>0) {
// init
initOtherInfo();
_contentPath = url;
// parse URL to determine what to do with it
var parseResults:ParseResults = parseURL(_contentPath);
var name:String = parseResults.streamName;
var canReuse:Boolean = canReuseOldConnection(parseResults);
_isRTMP = false;
_streamName = name;
ret = (canReuse || connectHTTP());
}
else
ret = super.connectToURL(url);
return ret;
}
}
Once we have this class ready, we need to let VideoPlayer know and use it to manage network communications. Otherwise, VideoPlayer will continue to use NCManager. VideoPlayer exposes a static property called iNCManagerClass. To replace NCManager with our custom class, we simply assign the property to the definition of the custom class. We have to do that prior to calling the load function. Here's the code snippet:
fl.video.VideoPlayer.iNCManagerClass = LimelightNCManager;
_video.load(videoURL);
Now, the video will play just fine even when videoURL is in Limelight secured url format.
Posted at 11:07 PM in AIR/FLex, Video | Permalink | Comments (1) | TrackBack (0)
May 19, 2010
My First Facebook Development Journey: Extended Permissions, FB AS 3.0 Client Library, and Application Tabs
On a recent project, I had to create a Facebook Flash application where the application will live in a Facebook application tab on a fan page. The whole Facebook application development was very new to me. During that time, because it was a Flash application, it seemed the most sense was to use the Facebook and Adobe supported Facebook AS 3.0 client library which can be found here.
My first impression was that the AS library did a pretty good job integrating the Facebook Platform API. The Facebook API calls were pretty straightforward. However, one issue I ran into using the AS 3.0 library was the requesting extended permissions feature. From what I learned, certain Facebook calls requires “extended permissions” where the user will need to explicitly give permissions to the application to retrieve/set certain Facebook data (one example is publishing a post on a user’s stream). To ask the user to extend permissions through the AS library, the statement would be:
facebook.grantExtendedPermission(ExtendedPermissionValues.PUBLISH_STREAM);
In the example above, what the user should expect is a popup window containing a request asking the user to extend the application’s permissions to publish posts on the user’s wall. However, if a user’s browser’s popup blocker is turned on, he/she will not see this popup window. Taking a closer look, the grantExtendedPermission method executes a navigateToUrl statement. The navigateToUrl method is known to be unfriendly with many browsers’ popup blockers. This does pose a major problem because if the user never sees the popup window that asks for extended permissions, then the Facebook application won’t work.
My next step to avoid the navigateToUrl issue was to execute some type of modal window through Javascript. And for the Flash application to communicate with the page, it will need to go through Flash’s ExternalInterface. After coding up the ExternalInterface functionality, the application spitted out a Security sandbox violation error. Facebook, at that time, did not support ExternalInterface.
I was still hopeful that there is a way. After doing some research, I found that to get around the ExternalInterface issue, Facebook does provide a flash bridge where a Flash application can communicate to the Facebook page through this bridge. More information on the bridge can be found here.
During my research, I learned that Facebook restricts pretty much any native Javascript, but the good news is that Facebook does have its own Javascript library called FBJS. Using FBJS allows me to use specialized methods. There was a specialized method to prompt user for extended permissions with optional callback functions:
Facebook.showPermissionDialog('publish_stream, read_stream', ...)
Eureka! I see the light at the end of the tunnel. After spending a couple of hours, I finally got the bridge to work. On the application canvas page (main application page), I was able to see my Flash application communicate to the Facebook page, and through the FBJS, the dialog box requesting for extending permissions appeared. Woohoo!
Now it is time to put the Flash application into a new fan page’s application tab. I went through all the required setup to set up a new tab. I opened the new tab, and ran through the flash application to the part where the extended permission dialog box should appear. When I arrived, I saw…. nothing. No dialog box. I double and triple checked the code and everything looked correct. More research ensued, and to my frustration, I learned that there are many restrictions that a Facebook application can do on an application tab compared to the application canvas page. Scouring through the Facebook forums, I learned other developers had issues with application tabs dealing with FBJS, and how certain data is not available to a Facebook application that lived in an application tab.
In the end, my solution was notify any users that comes to the application tab that he/she will need to allow popups from Facebook in order for the application to function. I found the this solution user unfriendly.
Alas, I do feel that there must be a way, but unfortunately, I hit a brick wall. If anyone were able to solve my conundrum, then I would love to hear from you. Any constructive feedback is always greatly appreciated!
Posted at 03:52 PM in AIR/FLex | Permalink | Comments (0) | TrackBack (0)
Papervision Wish List
I’ve been using Papervision 2.0 for several months now, and while I’ve been able to get it to do most of what I want it to, it has been a tedious journey. If I were to do this project over again, I’m not sure I would choose Papervision, unless they make some improvements.
I’ve put together a short wish list of things I’d like to see done to Papervision:
1. Documentation, Documentation, Documentation!
The ASDocs for Papervision are some of the worst I’ve ever seen. There is virtually no useful information there. While there are many tutorials on the web for various functions, a responsive mailing list (papervision3d@osflash.org), and a good book (Papervision3D Essentials by Paul Tondeur and Jeff Winder), the ASDocs really need to be maintained for people that want to do anything complicated.
2. Fix Z-Sorting Performance and Functionality.
I’ve been working on a room planner application where users can dynamically add products to a room, and z-sorting has been a bugaboo. I’ve been forced to use the QuadrantRenderEngine, which is a CPU hog and also disables the use of filters (like a ‘glow’ around a selected object). I’ve had to optimize this by turning rendering off when not needed and selectively using the QuadrantRenderEngine (by setting testQuad=false on DisplayObject3Ds). This renderer needs some attention to improve performance.
3. How Do I Create a Collada Object?
Should I use DAE, SkecthupCollada or the Collada class for my Collada objects (answer: DAE)? Why are there 3 classes where there should only be one? Enough said.
4. Add Height, Width and Depth to All DisplayObject3D Classes.
This seems like a no-brainer, but sadly this info is missing, even for the Cube class. I added it myself for the project I am working on. Note you can dig out the axis aligned bounding box (aabb) in the geometry property of some DisplayObject3D classes, but unfortunately not all, and not DAE (Collada) objects.
5. Can We Have a Class to Create 3D Objects from a Vertex Array, Please?
Sometimes I would just like to create a 3D object from a set of vertices. I haven’t found an easy way to do that with Papervision. It would be nice if there was a class that supported the creation of a 3D object from a vertex array passed in the constructor.
Well, that’s my list for now. Hopefully some of the Papervision developers will read this and consider these enhancements and fixes. If you have any of your own, please feel free to tag them on in a comment.
Posted at 11:18 AM in AIR/FLex | Permalink | Comments (0) | TrackBack (0)
January 05, 2010
Maintain Image Aspect Ratio Programmatically
The aspect ratio of an image is the ratio of its width to height, and all images have an inherent aspect ratio. By default, the <mx:Image> tag property maintainAspectRatio is set to true. This setting preserves the aspect ratio so that an image does not appear distorted.
<mx:Image maintainAspectRatio="true" />
So, if an <mx:Image> tag has defined an explicit width and/or height, the control will preserve the aspect ratio accordingly.
ie. <mx:Image width="100" height="100" />
However, there is a catch to the maintainAspectRatio property. Documented in the Adobe Livedocs, it states that when maintaining aspect ratio, there is a possibility there there will be "empty" pixels. For example, if an image is 100x100 pixels, and the <mx:Image> tag is defined as:
<mx:Image source="example.jpg" height="150" width="200" />
Then Flex sizes the image to 150 by 150 pxiels, the largest posible image that maintains the aspect ratio and conforms to the size constraints. The other 50 by 150 pixels remain empty. However, the <mx:Image> tag reserves the empty pixels and makes them unavailable to other controls and layout elements.
This can be an issue when when the <mx:Image> tag is wrapped in a defined "center" horizontal-aligned, "middle" vertically-aligned container, and the images can be of varying dimensions. In the example below, the original image is 380x169 pixels:
<mx:Box width="400" height="400" backgroundColor="#FFFFFF" horizontalAlign="center" verticalAlign="middle">
<mx:Image source="example1.jpg" height="25" maintainAspectRatio="true" />
</mx:Box>
Here, the <mx:Image> tag preserves the aspect ratio where the height is 25 pixels. However, the image is now not centered horizontally and vertically.
Image using the maintainAspectRatio
To go about this, one solution is to not rely on the <mx:Image> tag maintainAspectRatio property, and to programmatically determined the explicit width and height.
<mx:Box width="400" height="400" backgroundColor="#FFFFFF" horizontalAlign="center" verticalAlign="middle">
<mx:Image source="example1.jpg" height="25" maintainAspectRatio="false" complete="handleComplete(event)" />
</mx:Box>
Here, set the maintainAspectRatio to false so the <mx:Image> tag will not be handling the aspect ratio maintenance. Add a complete event handler so that when the image is loaded, we can calculate the aspect ratio:
private function handleComplete(event:Event):void
{
var target:Image = event.target as Image;
// Formula: The product of ratio and a known dimension.
target.width = (target.contentWidth * target.height) / target.contentHeight;
}
The width is defined as the product of the ratio and the known height. And as you can see, the image preserved its aspect ratio and is horizontally and vertically centered.
Image's dimensions determined programmatically
This is a simple example. There may be occasions where an <mx:Image> tag has both a defined explicit width and height. If that is the case, then we will need to add logic inside the complete event handler to determine what the explicit width and height is suppose to be in order to maintain aspect ratio.
Happy coding!
Posted at 11:52 AM in AIR/FLex | Permalink | Comments (0) | TrackBack (0)
December 04, 2009
Building a Better TileList: A Custom Scrollable TileList using Flex
I use a TileList component quite often when building Flex applications. Recently, I was asked to create a nice, smooth, horizontal scrolling effect for a TileList containing a 3x3 page grid of 40 or so images. After many unsuccessful attempts to cajole the TileList into the proper behavior, I decided to roll my own.
It seems the fundamental problem with the TileList, which extends from the ListBase class, is its management of item renderers. It gets confused when attempting an animation to change the horizontal scroll position using an AnimateProperty effect. It seems that item renderers get confused when moving items into view using an effect.
After futzing around with the offscreenExtraColumns and other properties, I still could not get the behavior I desired out of the standard TileList. So I built my own.
Now, I must state that the ScrollableTileList class that I created does have some limitations; it pre-loads an item renderer for each element in the dataProvider. This means that it isn't suited for very large lists or lists of unknown size which could grow large. But it works very well for lists that are of a known, somewhat small (~100 items) size. It is also left up to the item renderer to implement a selected property, which is used to highlight the selected item in the list. The current implementation does not have a default item renderer; you must supply one.
The ScrollableTileList is geared to swap directly in for a 'normal' TileList, so you'll see familiar properties like columnCount and rowCount. Another nice feature is more precise control over horizontal and vertical gaps between tiles, which is not well supported by the native TileList component.
I've provided a sample application that uses the ScrollableTileList here. The source code is included: Just right click on the demo and choose 'View Source' from the Flash menu.
Enjoy!
Posted at 10:59 AM in AIR/FLex | Permalink | Comments (2) | TrackBack (0)
October 23, 2009
Wrangling Flex Labels: Auto-sizing Text
I am currently working on a project where we have a fixed-width dialog box, and need to fit the title text in a label. The problem is, the text is too long. What to do? Well, the basic Flex Label provides you a couple of options: show an ellipsis (…) at the end of the label or cut the text off. Neither of these solutions was viable.
This is a common problem that occurs in UI development: fitting text in a limited space. My solution to this was to create a LabelUtil class that automatically adjusts the font size of the text to fit the width of the label.
Here is the meat of the code:
import
flash.text.AntiAliasType;
import
flash.text.Font;
import
flash.text.GridFitType;
import
flash.text.TextLineMetrics;
import
mx.controls.Label;
import
mx.core.Application;
import
mx.core.UITextFormat;
import
mx.styles.CSSStyleDeclaration;
import
mx.styles.StyleManager;
public class LabelUtil
{
public function LabelUtil()
{
}
static public function constrainTextToWidth( label : Label, htmlText : String ) : void
{
var style : CSSStyleDeclaration =
StyleManager.getStyleDeclaration("." +
label.styleName);
var fontSize : Number = style.getStyle( "fontSize" ) as Number;
label.setStyle( "fontSize", fontSize );
label.htmlText = htmlText;
label.invalidateSize();
label.validateNow();
while (
getTextWidth( label.text, fontSize, style ) > label.width )
{
fontSize =
fontSize - 0.5;
label.setStyle(
"fontSize", fontSize );
}
}
static public function getTextWidth( text : String, fontSize
: Number, style : CSSStyleDeclaration ) : Number
{
var textFormat
: UITextFormat =
new
UITextFormat(
Application.application.systemManager,
style.getStyle(
"fontFamily" ),
fontSize,
null,
style.getStyle(
"fontWeight" ) == "bold",
style.getStyle(
"fontStyle" ) == "italic",
null,
null,
null,
null,
style.getStyle(
"paddingLeft" ),
style.getStyle(
"paddingRight" ),
style.getStyle(
"textIndent" ) );
textFormat.antiAliasType
= flash.text.AntiAliasType.ADVANCED;
textFormat.gridFitType
= flash.text.GridFitType.PIXEL;
var textMetrics
: TextLineMetrics = textFormat.measureText( text );
return
textMetrics.width;
}
}
OK, so what’s going on here? First we pass in the label in question and the html text to be displayed (note the function will also work for plain text). Then, we measure the width of the text and compare it to the width of the label. If the text is narrower that the label, we’re done. Otherwise, we reduce the font size by a half-point until the text width is less than the label width.
There are a couple of things to consider using this method: First, you must specify the width of your label in pixels (this shouldn’t be a problem as this solution applies to a fixed width label). Also, you’ll need to make sure to define a CSS style for the label (you could modify the method to use the label’s inherent style properties, but I didn’t need to).
Notice that I am using a UITextFormat object to measure the text width. Why is that, when the label provides a textWidth property? The answer is that textWidth is unreliable in the label control. This method is rock solid.
Hope this helps your labeling needs. Happy Coding!
Posted at 01:45 PM in AIR/FLex | Permalink | Comments (1) | TrackBack (0)
October 01, 2009
Going with the Flow: Tips and Tricks for Using the Adobe Flex TextFlow Component
We've been busily developing using Flex 4 (aka “Gumbo”) here at the Grio offices and are impressed with many of its new features. One of the more interesting but difficult to decipher features is the TextFlow component.
The TextFlow component is used to layout text in a highly controlled way. It uses an XML-based markup language, Text Layout Format (TLF), to define the content of the TextFlow. TLF uses some similar tags to HTML (<div>, <p>, <span>), but it is definitely not HTML. This may trip you up a bit as you learn TLF; I recommend reading the Adobe Labs info before diving too deep into development (http://labs.adobe.com/technologies/textlayout/).
Adobe Labs also provides a nice text editor that can read and write TLF (http://labs.adobe.com/technologies/textlayout/demos/ ; a note of caution: the editor is somewhat out of date, so the TLF generated may not work perfectly with the latest text layout package).
One important thing to note is that The HTML to TLF conversion capabilities built into the Text Layout package are crap. They do not support much of anything, except paragraphs. Lists are not supported, and CSS is not either. For our last project, we basically built our own translator to generate TLF from HTML.
Which leads me to reveal my trick to render bulleted lists in TLF. The keys are: 1) The Unicode bullet character (\u2022); 2) A negative paragraph textIndent format attribute; and 3) The paragraphStartIndent attribute.
Here’s the format definition for each list item:
<format id='list_item' paragraphStartIndent='15' textIndent='-9' paragraphSpaceAfter='10'/>
Note that we have a negative textIndent and a positive paragraphStartIndent. This gives the desired list effect. You will need to adjust these numbers based on the size and face of the font you are using.
And here’s the function that converts the HTML list to a TLF list:
private static function appendList(htmlListNode:XML, tlfXml:XML, isOrdered:Boolean):void {
//used for ordered lists
var count:int = 1;
var listEl : String = isOrdered ? "ol" : "ul";
//get the li tags
for each (var child:XML in htmlListNode.*) {
var lineItemXml:XML;
var listItemContent:String = child.children().toXMLString();
// Ensure content for list item
if (listItemContent.length == 0) continue;
listItemContent = listItemContent.toLowerCase();
listItemContent = listItemContent.replace("<strong>","<span fontWeight='bold'>");
listItemContent = listItemContent.replace("</strong>","</span>");
if (listItemContent.indexOf("<ul>") > -1) {var preNestedListText:String = listItemContent.substring(0,listItemContent.indexOf(listEl));
var nestedList:String = listItemContent.substring(listItemContent.indexOf(listEl), listItemContent.length);
var nestedListXml:XML = new XML(nestedList);
if(preNestedListText.length > 0) {if(isOrdered) {
tlfXml.appendChild(new XML("<p listitem='true' format='list_item'>" + count + ". " + listItemContent +"</p>"));
} else {
tlfXml.appendChild(new XML("<p listitem='true' format='list_item'>\u2022 " + listItemContent + "</p>"));
}
}
appendList(nestedListXml, tlfXml, false);} else {
if(isOrdered) {
tlfXml.appendChild(new XML("<p listitem='true' format='list_item'>" + count + ". " + listItemContent +"</p>"));
} else {
tlfXml.appendChild(new XML("<p listitem='true' format='list_item'>\u2022 " + listItemContent + "</p>"));
}
count++;}
}
}
In summary, TLF is a very powerful text formatting language, but it currently lacks good HTML conversion tools and simple tags for things such as lists. We hope Adobe provides support for these in the future, but until then we’ll just have to code around the problem.
Posted at 02:14 PM in AIR/FLex | Permalink | Comments (2) | TrackBack (0)
March 21, 2009
Keeping it Clean: Creating a Profanity Filter with Flex
I was recently tasked with writing a profanity filter for the chat module of an AIR application. I did some research and alas, there were no Flex examples to be found. I thought I’d share my implementation with you.
The filter needed to replace naughty words with asterisks: so profanities such as ‘f--- you’ would appear as ‘**** you’. The filter also required the ability to use localized word ‘blacklists’.
I utilized two key functional areas of Flex to help me: regular expressions and resource bundles. I used regular expressions to search for naughty words within the input string, and resource bundles store the localized black lists.
Here’s a look at the core algorithm:
public static function cleanseChatText(inputString:String):String {
if (chatBlackList == null) {
chatBlackList = ResourceManager.getInstance().getStringArray("resources", "chat_Blacklist");
}
for each (var word:String in chatBlackList) {
var replStr:String = createReplacementWord(word);
// check if string is a naughty word
var regex:RegExp = new RegExp("^" + word + "$", "gism");
inputString = inputString.replace(regex, replStr);
// check if string starts with naughty word
regex = new RegExp("^" + word + "(\\W)", "gism");
inputString = inputString.replace(regex, replStr + "$1");
// check if string ends with naughty word
regex = new RegExp("(\\W)" + word + "$", "gism");
inputString = inputString.replace(regex, "$1" + replStr);
// check if naughty word is in string
regex = new RegExp("(\\W)" + word + "(\\W)", "gism");
inputString = inputString.replace(regex, "$1" + replStr + "$2");
// or other words start with naughty word (ignore short stuff)
if (word.length > 3) {
regex = new RegExp("(\\W)" + word, "gism");
inputString = inputString.replace(regex, "$1" + replStr);
regex = new RegExp("^" + word, "gism");
inputString = inputString.replace(regex, replStr);
}
}
return StringUtil.trim(inputString);
}
So what’s going on? First, we get the localized black list (if we haven’t already cached it). I‘m using the getStringArray()function of the ResourceManager; this returns an array of strings from a comma or otherwise delimited resource entry. After that, we loop through the word list and look for words to replace, using several regular expressions to match the word in various locations of the input string.
You’ll notice there are “$1” and “$2” in the replacement strings. What’s that? It’s Flex’s nomenclature for accessing matched patterns. For example “(\\W)” (which is any non-word character, i.e. not a-zA-Z0-9) is represented by “$1” in the replacement string. So the string “what-the-bleep” is replace with “what-the-*****”, and the non-word characters are retained in the replacement string.
There’s one more handy function. This is used to generate the replacement strings. I use a Dictionary object to cache the replacement strings so I am not constantly rebuilding them. If you don’t want to asterisk out the words, you could modify this function to create a custom replacement word.
private static function createReplacementWord(word:String):String {
var replStr:String = replWordList[word];
if (replStr == null) {
replStr = "";
for (var i:int = 0; i < word.length; i++) {
replStr += "*";
}
replWordList[word] = replStr;
}
return replStr;
}
So that’s it! Now you have the tools to ‘keep it clean’ using Flex.
Posted at 09:51 AM in AIR/FLex | Permalink | Comments (2) | TrackBack (0)
March 07, 2009
Adding Sound Effects to Your Flex/AIR Application
It’s easy to add sounds to a Flex/Air application. Here we’ll see how to add whirrs, chirps, and bloops to your application’s button clicks and mouseovers. We’ll also see how to use Air’s EncryptedLocalStore to add mute and volume controls.
The first step is to create a sound manager for playing the sounds. The sound manager will make it possible to centrally apply user preferences like volume to all application sounds.
import flash.data.EncryptedLocalStore;
import flash.media.SoundTransform;
import flash.utils.ByteArray;
import mx.core.SoundAsset;
public class SoundManager
{
public static const DEFAULT_VOLUME:Number = 3;
public static function play(sound:SoundAsset):void {
var sndXfrm:SoundTransform;
var sndXfrmVal:Number = 0;
var muteBytes:ByteArray = EncryptedLocalStore.getItem("muted");
var volBytes:ByteArray = EncryptedLocalStore.getItem("volume");
//handle mute condition
if (muteBytes && muteBytes.readUTFBytes(muteBytes.length) == "true") {
//don't play a sound
return;
}
//handle volume condition (should be a val between 1 and 10)
if (volBytes) {
sndXfrmVal = parseInt(volBytes.readUTFBytes(volBytes.length));
if(sndXfrmVal == 0 || isNaN(sndXfrmVal)) {
//couldn't find a valid value
//in the ELS (the user has not set prefs yet)
sndXfrmVal = DEFAULT_VOLUME;
}
} else {
sndXfrmVal = DEFAULT_VOLUME;
}
sndXfrm = new SoundTransform(0.1 * sndXfrmVal);
sound.play(0, 0, sndXfrm);
}
}
The SoundManager takes a SoundAsset as an argument to the play function. This object references an embedded sound file.
Now that we have a sound manager, let’s create a special Button for our application that will play sounds. To do this we’ll extend the flex Button component. Users will hear sound.mp3 when they click the button. We could easily expand this class to play sounds for mouseovers as well.
import flash.events.MouseEvent;
import mx.controls.Button;
import mx.core.SoundAsset;
public class SoundEffectButton extends Button
{
[Embed(source="assets/sound.mp3")]
private var mouseClickSoundClass:Class;
[Bindable]
public var mouseClickSound:SoundAsset =
SoundAsset(new mouseClickSoundClass());
public function SoundEffectButton()
{
super();
}
override protected function clickHandler(event:MouseEvent):void {
super.clickHandler(event);
SoundManager.play(mouseDownSound);
}
}
Now that we have a button, let’s use it! Note that we are overriding the default sound.mp3 referred to in the SoundEffectButton class with a short burst of ACDC rock and roll.
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.core.SoundAsset;
[Embed('assets/acdc.mp3')]
private var acdcClass:Class;
private var acdc:SoundAsset = SoundAsset(new acdcClass());
]]>
</mx:Script>
<local:SoundEffectButton label="Test" mouseClickSound="{acdc}"/>
As you can see, ELS provides a simple means of communicating user preference changes without the trouble of dealing with controllers and explicit file reads.
<mx:HSlider id="volume"
minimum="1"
maximum="10"
value="1"
liveDragging="true"
showDataTip="false"
tickInterval="1"
snapInterval="1" >
<mx:change>
<![CDATA[
var bytes:ByteArray = new ByteArray();
bytes.writeUTFBytes(volume.value.toString());
EncryptedLocalStore.setItem("volume", bytes);
]]>
</mx:change>
</mx:HSlider>
Posted at 09:41 PM in AIR/FLex | Permalink | Comments (1) | TrackBack (0)