views:

1335

answers:

3

What is the best way to render to a UIComponent which hasn't been added to the stage? (I'm using UIComponents as renderers for objects, and want to render new copies for image export, filtering, etc.)

Two strategies I've seen/used so far include realizing the component to ensure it calls all the lifecycle methods:

  1. Add the component to Application.application, render with BitmapData.draw(), remove component. This is similar to what I've seen done for printing unrealized components as well.

  2. Add the component to a pop up window, render with BitmapData.draw(), dismiss popup after rendering complete.

I believe both of these just rely on the UI not refreshing while the current thread/event is executing, though (1) could also rely on the component being realized out of view.

Is there a better way?

+1  A: 

I'm pretty sure you can use the draw() method in BitmapData without having your component on the DisplayList.

For example is use it when I need to modify images I load with the Loader Class. In the init handler I create a BitmapData instance and draw the Bitmap from the loadInfo.content property, then copyPixels() or whatever I need to modify the loaded image

George Profenza
You can, but since the UIComponent is unrealized it hasn't laid itself out yet. So, as an example, its height and width are 0.
Michael Brewer-Davis
oh yeah! I remember something about the component measuring with parents, drawing children, invalidating, etc. Haven't used flex in a while. Can't you create the component, add it to the display list, but set visible to false and carry on drawing ?
George Profenza
A: 

So much of a UIComponent's layout can be tied to it's context. This is especially true for a lot of its derivatives (e.g. HBox) since the fluidity of the layout is tied to it's parent's size and the number of siblings sharing its parents space.

Additionally Flex can be a real pain to get to visually update. Often critical render functions aren't done synchronously ... there are callLater, callLater2 and other hacky approaches that make dealing with the auto-magical layout properties of UIComponents a major headache. Not even calling validateNow or updateDisplayList can guarantee that the layout will be correct on the current frame (instead of a few frames in the future).

I suggest the best thing you can do is not use a UIComponent and try and use a Sprite or other.

Your approach to attach it but make it invisible (alpha = 0, mouseEnabled = false, mouseChildren = false) is decent. You should listen for the FlexEvent.CREATION_COMPLETE callback before you are certain it is properly laid out. Then you can bitmapData.draw it and then remove it from the stage. If you must use UIComponents then I know of no better way.

James Fassett
+5  A: 

What I've used in the past with much success is the following:

  1. Create a new instance of your component
  2. Add an event listener for FlexEvent.CREATION_COMPLETE
  3. Set visible=false on the component
  4. Add the component as a child of the main Application
  5. When the component is created, the event listener function will be invoked. The rest of the logic should be put in / invoked from your event listener function
  6. Remove the event listener you added in step #2.
  7. Use ImageSnapshot.captureImage() or captureBitmapData() to capture the visual representation of the component.
  8. Remove the component from the main Application
  9. Process the image data as you need to.

I've used this to snapshot various Flex charting components for use in PDF's generated on the server side. After getting the BitmapData I use the PNGEncoder or JPEGEncoder classes to compress the data, then encode it in Base64 before uploading to the server.

cliff.meyers
Just a note, you might want to look into PurePDF to do the generation client side instead of server, but depends on what your doing. Great library though!
JTtheGeek