Styling the Flex ToggleButtonBar

March 9th, 2010 § 2 comments § permalink

Yesterday I was working with the ToggleButtonBar in Flex and I wanted to change its appearance. I went over to the trusty Flex 3 style explorer and made some adjustments until I got it looking how I wanted. I copied the generated CSS, pasted it into my stylesheet and was ready to move on. When I ran my application however, I noticed that most of the styles had not taken effect. WTF?, I said. After some poking around and trying various configurations I came up with what I think is an acceptable solution.

When you copy the CSS from the style explorer you'll get something like this:

ToggleButtonBar {
   buttonHeight: 41;
   buttonWidth: 109;
   buttonStyleName: "mytoggleButtonBarButtonStyle";
   firstButtonStyleName: "mytoggleButtonBarFirstButtonStyle";
   lastButtonStyleName: "mytoggleButtonBarLastButtonStyle";
   selectedButtonTextStyleName: "mytoggleButtonBarSelectedButtonStyle";
}
 
.mytoggleButtonBarButtonStyle {
   highlightAlphas: 0, 0;
   fillAlphas: 1, 1, 1, 1;
   fillColors: #333333, #333333, #ff9900, #ff9900;
   color: #ffffff;
   textRollOverColor: #ffffff;
   textSelectedColor: #ffffff;
   themeColor: #0000cc;
   fontSize: 14;
   fontWeight: normal;
}
 
.mytoggleButtonBarFirstButtonStyle {
   cornerRadius: 9;
}
 
.mytoggleButtonBarLastButtonStyle {
   cornerRadius: 9;
}
 
.mytoggleButtonBarSelectedButtonStyle {
   color: #ffffff;
   fontSize: 14;
   fontWeight: normal;
   fontStyle: normal;
   textDecoration: none;
}
 

What I found is you need to copy the styles from the .mytoggleButtonBarButtonStyle class to the other styles that are generated for you. Obviously keep in mind that you only want to copy the properties that are going to be the same for each state, otherwise you'll overwrite your changes. This worked for me, hope it helps.

FLARToolkit AR Example

March 10th, 2009 § 1 comment § permalink

Here's my first crack at augmented reality (AR) with the FLARToolkit. The monster head was created in Lightwave by my boss, brought into Blender and exported to Actionscript. Still lots of things to work out but still a very cool technology.

AR Monster Head

AR Monster Head

The Flash Platform and Cloud API’s Part I: Flickr

February 23rd, 2009 § 2 comments § permalink

The Internet is full of sites that offer information and services for their users to store and share data from their favorite web sites and articles to their family photos. Many of these sites also offer an application programming interface or API to access this data. For years this functionality was limited to the browser, however, new emerging technologies are allowing this information to break free from the browser to the desktop, mobile phones and devices. The Adobe Flash Platform is leading this initiative with tools like Flash, Flex and AIR. This series will take some of the most popular Internet services and demonstrate how to use their API's to create your own rich, engaging applications with the Flash Platform.

Part I of this series covers the ubiquitous Flickr API. If you've done much searching around you've probably found a fair number of Flickr applications including various search tools, data visualizations and just plain "cool to look at" applications. The Flickr API lends itself well to building rich apps because the data itself is intriguing and visually appealing, and there's lots of it. I'm going to show you how to build a relatively simple desktop application using Flash that taps into the vast Flickr photo library, grabs 50 random "interesting" photos and displays them "Poloroid style" on your desktop. The finished application is available for download from the Adobe Marketplace (for free). Let's get started.

What you'll need:

1. Adobe® Flash®; download a 30 day trial
2. A Flickr account and API key, sign up at flickr.com
3. Flickr AS3 library, available here
4. Tweener AS3 library, available here


Here's a shot of what we're working towards.

Start by creating a new Flash file, name if flickr.fla and set the desired document properties. See the screen shot for the settings I used.

Because we're creating an AIR application that will run on the user's desktop we'll need to make a close button so they can exit out of the app when they're finished. Create a new movie clip named cmdClose (Insert > New Symbol...). Be sure the "Export for Actionscript" box is checked. Now build out your button in the design view. It can be as simple or as complex as you want. Be creative.

Go back into the main properties for you document. Where it says "Class" or "Document Class" if you're using CS3 type "Flickr"

Save your file.

Create a new Actionscript file and name it Flickr.as. Be sure to save it in the same directory as your FLA. This is where we're going to write all of the code for our application. If you're not familiar with how to use document classes in your Flash applications check out this tutorial at gotoandlearn.com . First we need to setup our class file, the basic structure is as follows:

package {
    import flash.display.MovieClip;
 
    public class Flickr extends MovieClip {
 
        public function Flickr() {
 
        }
 
    }
}

Next we'll need to import some classes that we'll be using to create our application. Add the following code right after your class declaration.

    import caurina.transitions.*; //import the tweener classes
 
    import com.adobe.webapis.flickr.methodgroups.Interestingness; //import flickr classes
    import com.adobe.webapis.flickr.FlickrService; //import flickr classes
    import com.adobe.webapis.flickr.events.FlickrResultEvent; //import flickr classes
 
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.display.Sprite;
    import flash.display.Loader;
    import flash.net.URLRequest;
    import flash.filters.DropShadowFilter;
    import flash.display.DisplayObject;

This code adds references in our class to the external classes we need to build out the various components of our app. Without these references our code would not compile because it wouldn't know what we were talking about when we tried to create a new tween for example. Next we need to declare some variables that we'll need in our code. After your import statements add the following variable declarations. I'll explain what they're used for as we need them.

    private const API_KEY:String = "ENTER YOUR OWN API KEY HERE";
    private var flick:FlickrService;
    private var flickrId:String;
    private var photoUrl:String;
    private var ldr:Loader;
    private var photoList:Object;
    private var index:uint;
    private var lastIndex:uint;
    private var lastX:Number;
    private var lastY:Number;
    private var curImg:DisplayObject;
 

Now that we have some of the ground work laid let's start working on the interface. The first function you created is the constructor function for you document class. This code will run when the app loads. We've already "stubbed" it out now let's add some code. First let's setup our close button.

    public function Flickr() {
        var btnClose:cmdClose = new cmdClose();
        btnClose.x = 949;
        btnClose.y = 22;
        btnClose.buttonMode=true;
        btnClose.useHandCursor=true;
        this.addChild(btnClose);
        btnClose.addEventListener(MouseEvent.CLICK,doClose);
 
        flick = new FlickrService(API_KEY);
        flick.addEventListener(FlickrResultEvent.INTERESTINGNESS_GET_LIST,interestingNessResult);
 
        flick.interestingness.getList(null,null,50,Math.random()*10);
   }

Here we're creating an instance of the button we built in our main FLA file, setting some initial properties including the x, y coordinates, the buttonMode and telling it to use the hand cursor when the user hovers over it. Next we add our button to the stage and finally we add an event listener so we can tell when the button has been clicked and we can call the appropriate function to close the application. After setting up our button we created an instance of the FlickrService object. This is the object we will use to communicate with the Flickr API service.

flick = new FlickrService(API_KEY);

The next line adds an event listener to our newly created 'flick' object. This tells our object to be on the lookout for anything that broadcasts a FlickrResultEvent (specifically, the 'INTERESTINGNESS_GET_LIST' event). When that event is broadcast our event listener will fire off the interestingNessResult function.

flick.addEventListener(FlickrResultEvent.INTERESTINGNESS_GET_LIST,interestingNessResult);

Finally we tell our object to actually go get the data we're requesting by calling the interestingness.getList method. This method takes the following parameters;

* date: (optional) A specific date, formatted as YYYY-MM-DD, to return interesting photos for. - we don't care so we pass in null
* extras: (optional) A comma-delimited list of extra information to fetch for each returned record. Currently supported fields are: license, date_upload, date_taken, owner_name, icon_server, original_format, last_update, geo, tags, machine_tags, o_dims, views, media. - we don't care so we pass in null
* per_page: (optional) Number of photos to return per page. If this argument is omitted, it defaults to 100. The maximum allowed value is 500. - we want 50 images so we pass in 50
* page: (optional) The page of results to return. If this argument is omitted, it defaults to 1. - we want a random page between 1 and 10 so we generate a random number within these bounds.

flick.interestingness.getList(null,null,50,Math.random()*10);

Now let's write our results code. The first function is pretty simple, it just sets up an object to store the photo data returned from the Flickr service call. Then it calls another function that actually does the work of laying out the photos on the screen.

private function interestingNessResult(e:FlickrResultEvent):void {
        photoList = new Object();
        photoList = e.data.photos;
        loadImages(0);
    }

Since this is a tutorial on using the Flickr API I'm not going to cover all of the code to layout the photos on the screen but I will cover some of the key things related to the Flickr results. The code you will get back should resemble the XML below. If your data looks different you may have an error somewhere in your code. A list of error codes can be found here .

 
<photos page="2" pages="89" perpage="10" total="881">
<photo id="2636" owner="47058503995@N01"
		secret="a123456" server="2" title="test_04"
		ispublic="1" isfriend="0" isfamily="0" />
<photo id="2635" owner="47058503995@N01"
		secret="b123456" server="2" title="test_03"
		ispublic="0" isfriend="1" isfamily="1" />
<photo id="2633" owner="47058503995@N01"
		secret="c123456" server="2" title="test_01"
		ispublic="1" isfriend="0" isfamily="0" />
<photo id="2610" owner="12037949754@N01"
		secret="d123456" server="2" title="00_tall"
		ispublic="1" isfriend="0" isfamily="0" />
</photos>

The loadImages() function loops through the object we created to store the photo results, calls a function to build the image URL and then loads the image. Because we need to wait for the image to load before we add it to the screen we add an event listener to the loader object to handle this.

private function loadImages(index:uint):void
        {
            if(index < photoList.photos.length)
            {
                ldr = new Loader;
                ldr.load(new URLRequest(buildPhotoUrl(photoList.photos[index].server,
                                        photoList.photos[index].id,
                                        photoList.photos[index].secret)));
                ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
            }
        }

Because Flickr stores photos on various servers we need to use the different pieces of data associated with a photo to build the path to the image. The function below does that for us.

private function buildPhotoUrl(server:String,photoId:String,secret:String):String{
            photoUrl = "http://farm1.static.flickr.com/" + server;
            photoUrl += "/" + photoId + "_" + secret + ".jpg";
            //trace("photo url = " + photoUrl);
            return photoUrl;
        }

There are many more methods available in the API than what we covered here but they are all used in essentially the same manner;

1. Import the necessary classes and libraries
2. Create your Flickr object
3. Call the appropriate methods
4. Handle the results when they come back.

With relatively little difficulty you can have access to millions of photos available on Flickr right in your own application. The full code for the PhotoPile application is given below.

  1. package {
  2. import flash.display.MovieClip;
  3.  
  4. public class Flickr extends MovieClip {
  5. import caurina.transitions.*;
  6. import com.adobe.webapis.flickr.methodgroups.Interestingness;
  7. import com.adobe.webapis.flickr.FlickrService;
  8. import com.adobe.webapis.flickr.events.FlickrResultEvent;
  9. import flash.events.Event;
  10. import flash.events.MouseEvent;
  11. import flash.display.Sprite;
  12. import flash.display.Loader;
  13. import flash.net.URLRequest;
  14. import flash.filters.DropShadowFilter;
  15. import flash.display.DisplayObject;
  16.  
  17. private const API_KEY:String = "[ENTER YOUR API KEY HERE]";
  18. private var flick:FlickrService;
  19. private var flickrId:String;
  20. private var photoUrl:String;
  21. private var uname:String;
  22. private var ldr:Loader;
  23. private var photoList:Object;
  24. private var index:uint;
  25. private var lastIndex:uint;
  26. private var lastX:Number;
  27. private var lastY:Number;
  28. private var curImg:DisplayObject;
  29.  
  30. public function Flickr() {
  31. var btnClose:cmdClose = new cmdClose();
  32. btnClose.x = 949;
  33. btnClose.y = 22;
  34. btnClose.buttonMode=true;
  35. btnClose.useHandCursor=true;
  36. this.addChild(btnClose);
  37. btnClose.addEventListener(MouseEvent.CLICK,doClose);
  38.  
  39. flick = new FlickrService(API_KEY);
  40. flick.addEventListener(FlickrResultEvent.INTERESTINGNESS_GET_LIST,interestingNessResult);
  41.  
  42. flick.interestingness.getList(null,null,50,Math.random()*10);
  43. }
  44.  
  45. private function doClose(e:MouseEvent):void {
  46. this.stage.nativeWindow.close();
  47. }
  48.  
  49. private function interestingNessResult(e:FlickrResultEvent):void {
  50. photoList = new Object();
  51. photoList = e.data.photos;
  52. loadImages(0);
  53. }
  54.  
  55. private function loadImages(index:uint):void
  56. {
  57. if(index < photoList.photos.length)
  58. {
  59. ldr = new Loader;
  60. ldr.load(new URLRequest(buildPhotoUrl(photoList.photos[index].server,
  61. photoList.photos[index].id,
  62. photoList.photos[index].secret)));
  63. ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
  64. }
  65. }
  66.  
  67. private function loaded(e:Event):void{
  68.  
  69. var sp:Sprite = new Sprite();
  70. sp.addChild(ldr.content);
  71. sp.x = 10;
  72. sp.y = 10;
  73.  
  74. // create a filter
  75. var dropShadow:DropShadowFilter = new DropShadowFilter(4, 45, 0x000000, 0.4, 8, 8, 2, 3);
  76.  
  77. var frame:Sprite = new Sprite();
  78. // Thickness of the stroke
  79. frame.graphics.lineStyle(0);
  80. // Draw the background layout box
  81. frame.graphics.beginFill(0xFFFFFF);
  82. // x, y, width, height
  83. frame.graphics.drawRect(0,0,sp.width + 20,sp.height+60)
  84.  
  85. frame.scaleX = frame.scaleY = .25;
  86. frame.x = Math.random()*(this.stage.stageWidth-frame.width);
  87. frame.y = Math.random()*(this.stage.stageHeight-frame.height);
  88.  
  89. frame.filters = [dropShadow];
  90. frame.addChild(sp);
  91. frame.addEventListener(MouseEvent.CLICK,doClick);
  92.  
  93. this.addChild(frame);
  94. frame.alpha = 0;
  95. Tweener.addTween(frame,{alpha:1,time:.25,transition:"linear"});
  96.  
  97. index++;
  98. loadImages(index);
  99. }
  100.  
  101. private function doClick(e:MouseEvent):void{
  102. var img:DisplayObject = e.currentTarget as DisplayObject;
  103. if(curImg == img)
  104. {
  105. this.setChildIndex(img,lastIndex);
  106. Tweener.addTween(img,{x:lastX,
  107. y:lastY,
  108. scaleX:.25,
  109. scaleY:.25,
  110. time:.5,
  111. transition:"easeOutCubic",
  112. onComplete:function() {curImg = null;}});
  113. } else {
  114. if(curImg != null){
  115. this.setChildIndex(curImg,lastIndex);
  116. Tweener.addTween(curImg,{x:lastX,
  117. y:lastY,
  118. scaleX:.25,
  119. scaleY:.25,
  120. time:.5,
  121. transition:"easeOutCubic"});
  122. }
  123.  
  124. lastIndex = this.getChildIndex(img);
  125. lastX = img.x;
  126. lastY = img.y;
  127. this.setChildIndex(img,index);
  128. Tweener.addTween(img,{x:((this.stage.width/2) - (img.width)),
  129. y:img.height/2,
  130. scaleX:1,
  131. scaleY:1,
  132. time:.5,
  133. transition:"easeOutCubic"});
  134. curImg = img;
  135. }
  136. }
  137.  
  138. private function buildPhotoUrl(server:String,photoId:String,secret:String):String{
  139. photoUrl = "http://farm1.static.flickr.com/" + server;
  140. photoUrl += "/" + photoId + "_" + secret + ".jpg";
  141. //trace("photo url = " + photoUrl);
  142. return photoUrl;
  143. }
  144. }
  145. }

New Blog Series

February 17th, 2009 § 0 comments § permalink

A while back I submitted a session for 360|Flex on Cloud API's and the Flash Platform. I didn't get selected but it's still something I'm interested in so I'm going to do a series of blog posts about instead. Each post will focus on a different Cloud API including but not limited to digg, WordPress and of course the ubiquitous flickr. If you have any requests please feel free to leave them in the comments and I'll do my best to accomodate.