Recently I was charged with the responsibility of creating a very complex visual interface in Flex that looks more like something you’d expect to see built with Flash because of the animation and 3D effects. Flex was clearly the better choice for developing this piece of the application, but I learned a number of things regarding the limitations of Item Renderers, particularly those having to do with the Flex TileList component.
The Requirement
The primarily component needed to display a tiled list of objects that represented a group of media objects. Each item renderer in the TileList had to have 3 images, each representing a different piece of media, and when the user hovered over one of the items, a parallel effect that combined one Zoom effect, 3 Move effects, and 2 Rotate effects. The top image ultimately needed to zoom in and have a DropShadow filter applied to it to give it a 3D-like effect, and the middle and bottom images needed to fan out to the left and right using the combined rotate and move effects on each one.
Getting the Data
Getting the data in the first place was a task in and of itself. My first several iterations of the code involved a lot of looping through the massive collection of media “packages” that is returned from the server, in order to get the sets of preview images from each media package object that i needed for the item renderers. I eventually came to realize that I could cut the amount of code I was using for this thing into a fraction by simply using the standard , with the data provider set to this massive collection of objects being sent from the server. This, however, required a complex and well-planned item renderer component written in ActionScript. The item renderer component extended the Flex TileListItemRenderer class and implemented the IListItemRenderer and IDropInListItemRenderer interfaces.
In this solution, each Item Renderer automatically receives one of the media package objects from the large collection that is sent from the server. This functionality is already built into the ListBase, so all I needed to do was find the array of images that from the object, then loop through the array and determine the “role” that each image should play in the item renderer (eg. top image, middle image, or bottom image) and discard anything else that I didnt need.
The Major Problem
The big problem I ran into, was the fact that the complicated effects that i built for the mouse over and mouse out required the item renderer display object to overlap the surrounding display objects in the tilelist. This, unfortunately, was completely impossible, and after studying all of the classes that TileList is extended from and how the TileList component automatically lays out the objects within the list, I found that it was impossible to prevent clipping of the images from occurring when the effects were generated.
The Quick-and-Dirty Solution
Due to time constraints, I needed a solution that i could implement quickly, and as long as it looked pretty, we were ok. In other words, an elegant hybrid component was simply not an option. I should also note that other possible solutions, like using a repeater with a Grid container, were not options either for reasons that are beyond the scope of this article/blog.
I created an mxml component based on the Canvas container that took 3 images as properties, dropped shadow filters on the images and included 2 parallel effects for the over and the out events. When the mouse over (or mouse out) occurs on an item renderer, this additional component that holds the effects is added to the stage with this.parentApplication.addChild(_effectContainer). So essentially, a new iteration of the item renderer that fired the event is being created as an overlay and the effect can then play and is free to run over any display objects in its way, since it is ultimately automatically added to the stage with an index that is higher than all other display objects on the stage.
In order to get this right however, the positioning of this canvas has to be pristine. In other words, when the new child (the effect container) with the duplicated images is added to the stage, the x,y coordinates of the initial state of the image stack (which is with only the top image displayed, assuming the other two images are of the same size and have the same x,y values) must be EXACTLY the same as the x,y position of the item renderer that generated the new instance of the display object responsible for displaying the effects. This ultimately makes it look as if the animation is playing on the actual item renderer, when in fact the item renderer is merely still lying directly beneath it’s double.
To be honest, the final product is pretty bad-ass and gives a flash-like quality to a data-driven Flex UI component. However, this is not the most elegant solution, as the ideal thing would be to create a hybrid TileList-like component that includes DYNAMIC layout constraints on each item renderer. Therefore, the component would automatically recognize that a display object inside of one of the item renderers was pushing on the boundaries of the container that was allocated to it and resize itself automatically until the animation stopped by placing calling super.invalidateProperties or super.updateDisplayList on the UIComponent class on each ENTER_FRAME event until the internal display objects of the item renderers stopped pushing on the layout bounds. There is a little caveat to this however, and that is – resizing the layout constraints of the item renderer must NOT change the layout of the component as you would expect. In other words, we could suspect that implementing such functionality into a hybrid TileList component would by default push the other item renderers around to compensate for the renderer that was resized. This assumes however, that we do NOT want the item renderer to overlap the surrounding renderers, which in the case of this requirement, is untrue. When the user rolls off the item renderer’s effect double, the rollover animation should basically play in reverse and then simply remove itself from the parent’s display list. Thus, the original layout of the TileList is never changed by the effects rendering.
My feeling is that we will build such a component eventually, probably for a future version of this application, but we’ve got a deadline to meet right now, and sometimes making the right business decision means quick and dirty is the answer.
As much as I’d love to actually show the code for this view state that I have described here, I must respect certain nondisclosure and intellectual property rights and restrictions considering the nature of the application we are building. However, I will provide a generalized code example of a specific element described in this article if there is something in particular that you would like to understand in greater detail by actually seeing it. I am very quick to respond to comments, so feel free to ask if there is anything in particular you would like to see.
Possibly Related Posts:
Posted by Dan Orlando on November 10th, 2008 :: Filed under
TutorialsTags ::
AIR,
Flex,
Flex UI components,
Item renderers,
list-based components,
TileList