Day 3: Here Comes Trouble

I finally got the user interface laid out without having to port any of the Flex 1.5 version files. This was a good thing because it forced me to use the Flex Builder 2 design tools and it made things much easier. The next task is to get the program to connect to Flickr and return some pictures.

As I suspected, it wasn’t going to work right away. The com.macromedia.flickr.Flickr class was by and large fine. Instead of passing in a reference to an HTTPService that was created in the main application, the Flex 2 Flickr class creates an instance of HTTPService in its constructor function. It also used addEventListener to hook up result and fault handlers.

Delete Delegate

One thing I forgot to mention in my Day 1 notes is that you no longer need to use mx.utils.Delegate to scope your event handlers.

In other words, when you do this in Flex 1.5:

button.addEventListener("click",mx.utils.Delegate.create(this,doSearch));

you now do this in Flex 2:

button.addEventListener("click", doSearch);

ActionScript 3 now knows how to scope the functions properly.

Back to the Story

When I first created the Flex 1.5 PhotoSearch program, I found it a little confusing as to how you log in or authorize the application. But I followed the instructions on the Flickr site and got the program to work smoothly. So I used the same code with a minor change: getURL has been deprecated in favor of flash.network.navigateToURL which is just as simple to use.

I ran the program and the first phase, which gets what Flickr calls a FROB, returned what appeared to be a valid string. At least no error was returned and the value looks like it ought to look. The next phase involves taking that frob and, along with my application’s API key and shared secret, uses navigateToURL to authenticate the session and get a token. This works like a charm in the Flex 1.5 version.

But in the Flex 2.0 version I cannot get it to work consistently. More than half the time I get an error from Flickr telling me my signature is incorrect. But then other times it works fine. I even ran the Flex 1.5 version in the Flash Player 8.5 Alpha and it worked consistently.

I tried everything, including getting a new API key from Flickr. Nothing makes it work consistently.

So I decided to abandon the Flickr authorization process for now. This is acceptable because PhotoSearch doesn’t really need to be authenticated because it does not modify anything – it is just a search tool. I didn’t realize this when I was first writing the Flex 1.5 version and I may go back and remove the need to authenticate from that version of PhotoSearch.

Since I didn’t need to get the application authorizated, I still wanted to use States, so I replaced the AuthorizationWindow with a WelcomeWindow which just explains the program a little.

Welcome

Thumbnails

Now that the trial of getting into Flickr has been resolved, I wanted to get searching by tags to work and displaying some images.

Remember that this version is using a TileList which is a control and not the Tile which is a container. This means my Thumbnail component now has to be a renderer. This turns out to be very easy in Flex 2.

A simple Thumbnail could look like this:

<mx:Canvas width="104" height="104" xmlns:mx="..." >
<mx:Image x="2" y="2" source="{dataObject.thumbURL}" />
</mx:Canvas>

If the dataProvider to the TileList is a collection of objects, each of which has a field called “thumbURL” that points to a remote image, then the TileList will display the thumbnail.

But I want this to be a bit more complicated because I want the user to see “Loading” while waiting for the image to display. So the actual Thumbnail has 2 states: loading and image. The loading state has a Label component with the word, “Loading” in it. The image state has the image of the thumbnail. Both the image and the label are contained in a Canvas and the Canvas provides the root tag for my Thumbnail.

I created the two states and set the image’s complete event to switch from the loading state to the image state. But as I mentioned in the last article, I need to switch back to the loading state when a new search is done or else the images will not change (or at least, they won’t show “Loading” while the user waits).

The Thumbnail now looks like this:

<mx:Canvas width="104" height="104" xmlns:mx="...">
<mx:states>
<mx:State name="imageState">
<mx:RemoveChild child="{loadingLabel}" />
<mx:PropertyOverride target="{img}" property="visible" value="true" />
<mx:PropertyOverride target="{img}" property="alpha" value=".8" />
</mx:State>
</mx:states>
<mx:Script>
<![[
[Bindable]
public var thumbURL:String;
override public function set dataObject( value:Object ) : Void {
super.dataObject = value;
currentState = "";  // reset
thumbURL = "http://static.flickr.com/"+dataObject.server+"/"+dataObject.id+"_"+dataObject.secret+"_t.jpg";
}
]]>
</mx:Script>
<mx:Image id="img" x="2" y="2" complete="currentState='imageState'" source="{thumbURL}" visible="false" />
<mx:Label id="loadingLabel" text="Loading" y="44" width="100%" textAlign="center" />
</mx:Canvas>

Notice that the source for the Image is bound to the variable thumbURL. Then note that the setter for the dataObject property has been overriden (it took me a number of hours before I went back to the documentation and found this). This override does 3 important things:

  1. it invokes the super.dataObject setter method;
  2. it sets the state to the base state, causing the image to become invisible and the label to reappear;
  3. it creates the thumbURL variable from the items in the dataObject. In the Flex 1.5 version I created this value in the Gallery when I received the data from Flickr. Now this is encapsulated as part of the thumbnail – it knows how to form the correct URL.

The data binding on the image’s source property causes the image to be loaded. When the load completes, the state of the Thumbnail switches over to the image state: the label is removed and the image is made visible. When a new search is made the process repeats itself when the TileList calls upon the dataObject setter.

There seems to be a bug with the Image component in that the image’s width and height are reported to be zero when the complete event is fired. In the Flex 1.5 version, I use that event as an opportunity to center the image within the Thumbnail. I posted a question on the Flex 2 Framework forum and received a reply (thanks xxx) that I should try not img.width, but img.content.width. That worked, so now the Thumbnails appear centered once again. I will apply this technique to the ImageViewer when the time comes.

Making the Request and Processing the Results

When the Go Search button is picked, the text from the TextInput field is given to the Flickr class and then an HTTP request is made to the Flickr service.

When the result comes back, the Flickr class dispatches a com.macromedia.flickr.FlickrEvent which contains the results.

The PhotoSearch component has an event listener for this which looks something like:

private function searchComplete( event:FlickrEvent ) : Void {
totalPhotos = event.data.total; // for the bottom of the Gallery
totalPages = event.data.pages; // for the bottom of the Gallery
pageNumber.value = event.data.page; // sets NumericStepper

gallery.dataProvider = data.photo; // this is an array
}

The searchComplete method sets some variables that are data-bound to controls and then it passes the list of photos over to the Gallery.

The Gallery component has a setter called dataProvider which simply converts the Array of photos in the result to an ArrayCollection> which is bound to the TileList displaying the Thumbnails.

In Flex 1.5 you could use an Array as the dataProvider to a DataGrid or any other List control. In Flex 2, you need to use a collection so I converted the photos into an ArrayCollection. In the Flex 1.5 version I took the result list of photo information and created an Array of com.macromedia.flickr.PhotoInfo objects, which included setting the URLs to the thumbnail and full-size images. In the Flex 2 version, I do not need to use the PhotoInfo class (because Thumbnail and make its own URL).

Conclusion

At first I was disappointed that I could not get this Flex 2 version of PhotoSearch to authenticate with Flickr. But when I realized I didn’t need to do that, I switched gears and moved toward getting the thumbnails to display.

In the Day 2 journal entry I said that I did not want to port the Flex 1.5 application to Flex 2. Since I decided to use a TileList instead of a Tile container, this was a good decision. It made me rethink how the Thumbnail code works and it in fact simplified it. Using states in the Thumbnail made it easier (less code) to switch between displaying the “Loading” message and the image.

When a new search is made and the results come back, the TileList (or the Flex 2 framework) takes the contents of the dataProvider and invokes the dataObject setter on each renderer, aka Thumbnail. Because I overrode the setter, I was able to intercept this change, reset the state, and build a new URL for the new image.

Showing Photos

In the Flex 1.5, the setValue method takes care of that, but setValue also gets called on many other occaisons, such as when the mouse rolls over the cell, so it can be difficult to code setValue properly.

My conclusion at the end of this day is that Flex 2 is good stuff. Once over the learning curve, the code has become more streamlined and even more object-oriented.