Creating Liquid GUIs with Flash - Part 3
With user experience expectations on the rise, developers more and more are looking for ways to bring the rich experience of a desktop application to the web. This is part three of the tutorial about developing dynamic graphical user interfaces (GUI) that intelligently mold themselves to an end-user’s expectation.
Version 3
In version three, I dumb down the mcGUI class, as it really does not need to know how to implement the resizing of its children. Instead, the class will limit its capabilities to relocating children to proper locations and telling children what size to resize to. The first part remains the same, as mcGUI simply sets _x and _y locations of any children that need to move. In this case, the only clip moving is footer_mc in the vertical direction, so it continues to set footer_mc._y. Resizing is handled differently.
The first thing you will do is edit the header_mc, body_mc and footer_mc MovieClips. For header_mc, open the symbol for editing and select all of its contents on the stage. Convert the artwork to a new MovieClip and give it an instance name of bg_mc so that you have gui_mc on the main timeline which contains header_mc, which inturn contains bg_mc. Do the same for body_mc and footer_mc. The point here is we want the background artwork of each of these clips to be independent of any other elements in the clip. For example, when you later add a title to the header_mc, you can resize just the background without resizing the title. If you don’t do this, you would resize everything together, which is not the goal.
Since each component can potentially act as a container and contain children components who, in turn, may contain children components and so on, it makes more sense to let the child handle it’s own resizing by just telling it what size it is suppose to be. Each component we create is going to be its own custom class, just like I did for mcGUI, so we will use polymorphism and call the same method “resizeTo()” on all clips that need resizing. To do this, we need to create an interface that will be implemented by all classes that resize themselves. In the liquidgui folder, create a file named IResizable.as, open it for editing in Flash and add the following code:
interface com.jor.examples.liquidgui.IResizable { public function resizeTo (w:Number, h:Number):Void; }
Creating an interface is easier than creating a class in that there is no code. An interface simply defines the methods that need to be present in the class that implements it. Here you see that you require a method named resizeTo that returns Void and accepts two parameters, each a Number. The line ends with a semicolon and there are no curly braces like the implemented method will have. All interfaces need to be defined as public, since interfaces are what you use to expose your class to the outside world.
You will use this interface with each of the new classes. Create three new files in liquidgui named mcHeader, mcBody and mcFooter. Open those files for editing in Flash and add the following code:
dynamic class com.jor.examples.liquidgui.mcHeader extends MovieClip implements com.jor.examples.liquidgui.IResizable { // Private references to child objects private var bg_mc:MovieClip; // Implemented method of IResizable interface public function resizeTo (w:Number, h:Number):Void { bg_mc._width = w; } }
This is the code for the mcHeader class and, in the first few lines, you can see it extends MovieClip and implements the IResizable interface. Inside the class there is a private reference to its bg_mc MovieClip so it can access it. Then you see its required implementation of the resizeTo() method. For this class, the only thing it does when it resizes is set bg_mc._width to the width passed in as w. Now link this class file to the symbol in the library the same way you did the mcGUI class in the previous section. Right-click the mcHeader MovieClip in the library and select Linkage from the menu. Check “Export for ActionScript” and enter “com.jor.examples.liquidgui.mcHeader” in the textbox.
The other two classes, mcBody and mcFooter, are below and should look very similar to mcHeader. Add this code and link them to the appropriate symbols:
dynamic class com.jor.examples.liquidgui.mcBody extends MovieClip implements com.jor.examples.liquidgui.IResizable { // Private references to child objects private var bg_mc:MovieClip; // Implemented method of IResizable interface public function resizeTo (w:Number, h:Number):Void { bg_mc._width = w; bg_mc._height = h; } }
dynamic class com.jor.examples.liquidgui.mcFooter extends MovieClip implements com.jor.examples.liquidgui.IResizable { // Private references to child objects private var bg_mc:MovieClip; // Implemented method of IResizable interface public function resizeTo (w:Number, h:Number):Void { bg_mc._width = w; } }
Finally, you need to update the mcGUI class to make use of these new method calls instead of handling the resizing internally. Here’s the newly implanted onResize method of the mcGUI class:
public function onResize (e:Object):Void { // Get the new stage size var sw:Number = Stage.width; var sh:Number = Stage.height; // Move children to correct locations footer_mc._y = sh-fh; // Tell children to resize themselves header_mc.resizeTo(sw, hh); body_mc.resizeTo(sw, sh-hfh); footer_mc.resizeTo(sw, fh); }
Finally, add a new layer to the header_mc MovieClip. On this layer, create a static text field with the words “Image Gallery” in a style of your choice and position it in the top left corner of the clip. Now that we are resizing the background of the header_mc clip and not the whole clip itself, the title will not resize along with it. Launch version 3 to see how I’ve done this.
Version 4
In this final version of the gallery application, I add a lot more functionality. Since this functionality becomes complex and veers from the topic of creating a liquid GUI, I’ll just go over the main points and let you explore the source code in more detail.
- Header Section
- I add a series of radio buttons so that the user can toggle between an actual size state and a scaled size state. The buttons are created as MovieClips which extend a custom radio button class. When a button is clicked, a custom onChangeState event is dispatched which passes along an event object containing the name of the state to change to.
- Body Section
- In this section, I add an empty MovieClip to load the images into. The class mcBody also uses the State Pattern to toggle back and forth between states. By creating a concrete implementation of “actual size” behavior and separate concrete implementation of a “scaled sized” behavior, I could change the state of the mcBody class at run-time by simply assigning one state or another to the current state. Explaining the State Pattern in-depth deserves its own tutorial, so please review the source code to see how this was done as well as check out the Head First: Design Patterns book. The mcBody class also implements an Event listener and listens to events dispatched by both the Header and Footer.
- Footer Section
- In this section, I implement a simple loader to display thumbnail images one after another. Each thumbnail image is a new instance of an mcIcon class which implements onPress event handling. When a thumbnail is pressed a custom onLoadImage event is dispatched which passes an event object containing the path of the image to load.
[ Download MX2004 source code ] (final version only)
Where to Go from Here
I hope you found this tutorial fun and informative. The gallery application is a nice start for something that can really be embellished. Here is a short list of things you can do to improve upon this example:
- Design your own custom interface
- Have the Footer load in an image list from an external source such as XML
- Include metadata in the XML to display along side the image such as name, file size and description
- Add scrollbars to the body section that appear only when the image loaded is larger than the body’s area
- Add scrolling action to the thumbnail images when the window size doesn’t enable all of the thumbnails to view, or perhaps resize the thumbnails to fit
- Add a gallery chooser so that the user can switch between galleries within the same application
- Build something completely different using the same approach like an RSS news reader
- Share your creations and embellishments with me and others so we can all learn new things
October 13th, 2006 at 3:22 am
Thanks for the great set of tutorials, James. Coincidentally, I’ve been working with liquid layouts in a project I’m working on these days, too.
Unfortunately, there seems to be a stumbling block when using the Flash v2 components — particularly the DataGrid object — which refuses to resize itself correctly once its horizontal scrolling behavior has been activated. (My guess is the internal masking is raising havoc with the width properties) Any experience — or thoughts? — on how to scale such items correctly?
October 13th, 2006 at 7:40 am
With the UI components you need to use their setSize(w,h) method to resize them as opposed to using _width and _height.
October 15th, 2006 at 5:24 pm
Well, sonuva… Thanks! That helps out a lot. One of these days I’m going to find this magical book of exceptions to the rules.
October 16th, 2006 at 8:45 am
Hey James,
Nice work. This was a great use of the State Pattern in action. I like the thoroughness of your article as well.
Mims
October 16th, 2006 at 12:10 pm
good tutorials, and i have make some changes.
1. use a observer pattern on releation between class “mcGUI” and the observer.
2. the class mcGUI may be change to singlton
3. can use composition, instead of subclass of movieclip.
detail u can see here: http://blog.danielyuen.hk/articles/2006/10/17/liquid-gui-with-flash
October 16th, 2006 at 12:52 pm
Thanks, Mims.
Daniel, you’ve made some nice improvements and thanks for sharing them!! Using the Singleton Pattern and the Observer Pattern are definately good additions and ones I’ve seriously considered including myself. Ultimately though, I decided the additional design patterns were making the tutorial complex and I wanted to keep my tutorial short and basic so I regretfully left them out. I am however, glad you’ve added them, to give people even more ideas on what they can do here.
I’ve been getting some nice feedback on this tutorial both in comments and e-mail. I’m glad to see people are really enjoying it. I’m looking forward to hearing even more ideas, so keep them coming!
October 31st, 2006 at 3:56 pm
Great article James
)
Having trouble opening the FLA examples in your ZIP file.
I’m on Mac using Flash MX 2004.
Can you please take a peek for me?
OK to repond off-blog.
Cheers
)
Robert
October 31st, 2006 at 6:02 pm
The files are saved as Flash 8. Later tonight I’ll post a saved down version underneath the link and shoot you an e-mail when its available.
November 13th, 2006 at 5:46 pm
I was struggling to get my page based on the LiquidGUI examples to open up at 100%, using masks, etc, and finally see that if I go to your page “straight up” I have the same problem - the fit-to-page doesn’t happen until the first resize. (See http://www.jamesor.com/examples/LiquidGUI/Ver4/liquid_s4.html as a direct link)., As I am doing a page I’d like to open less forcefully, and like to open at larger than life, I now want to figure out how to do this: Open link to page ( http://www.picaza.net/c06/gallery.htm , changing constantly) anyhow, anywhere and have it start off at 100% of whatever the stage width is.
I’m working on it, but if anyone has delt with this and solved it, please let me know!
Thanks
Picaza
November 14th, 2006 at 2:00 am
Never mind, I got it! I believe the trick was to change mcGui.as to have a resizeAll function, and then call that function during a child creation (I put it in mcFooter.as). I also am using - SWFObject embed by Geoff Stearns geoff@deconcept.com http://blog.deconcept.com/ - that didn’t solve my problems, but it’s nice.
in mcGui.as:
———————————————————-
public function onResize (e:Object):Void
{
resizeAll();
}
public function resizeAll ():Void
{
// Get the new stage size
var sw:Number = Stage.width;
var sh:Number = Stage.height;
// Move children to correct locations
trace(”mcGui - resizeAll ” + sw + ” ” + sh);
footer_mc._y = sh-fh;
// Tell children to resize themselves
header_mc.resizeTo(sw, hh);
body_mc.resizeTo(sw, sh-hfh);
footer_mc.resizeTo(sw, fh);
}
—————————————————
in mcFooter.as
—————————————————
public function configUI ():Void
{
…… stuff…….
_parent.resizeAll();
}
Picaza
November 26th, 2006 at 7:14 am
Hey James,
Great, GREAT tutorial here! Thanks so much for the insight!
I’ve been fumbling around in the code, and I’m trying to add elements to both the header and footer that will stay centered as the screen is resized. I noticed that it does it perfectly within the Body section, where the image is loaded always dead center horizontally within the GUI. How can I achieve this within the header and footer as well? For example, I want my logo and interface navigation to also be centered within the footer of the GUI. Any insight would be greatly appreciated! You can also reply offline if you like.
Thanks, James.
December 14th, 2006 at 9:40 am
Hi, great tutorial, looks like i am going to really take some time with this one… but one question though,
is there a way to make the stage “adopt” a bigger sized-image, and rezie automtically? (lets say is the xml source has the (x,y) values, and then the gui streches on its own?
thanks
December 14th, 2006 at 12:04 pm
Chris: Just adjust your resizeTo() function of the component you want to address. Aside from changing the dimensions of the component, handle additional tasks such as relocating children of the component.
assaf: I’m not sure I understand your question. After you’ve had a chance to mess around with the code a bit (look at version 4), if you aren’t able to come up with an answer to your question, try reposting the question in a different way.
January 1st, 2007 at 3:29 pm
how do you initialize the resize of the whole UI so that when you open the page it resizes for the first time? right now when i open the page it doesnt set the interface how i want untill i do the first resize.
thanks
January 2nd, 2007 at 12:15 am
Place a call to the onResize () method inside the mcGUI’s onLoad method. This will force the resizing to happen once the app is loaded.
January 9th, 2007 at 11:06 pm
Hi thanks for your tutorial. I am very interested in Liquid Layouts in flash and have found very little information. Your’s is the only tutorial I’ve found that deals with a way to stretch the image within a certain limit. However I’m only a novice and it seems quite advanced.
I am working with the following code that I find simple to use
movie1._y = Stage.height/2;
movie1._x = Stage.width/2;
movie2._height = Stage.height;
and at the end of the code I have a listener
movie1 floats in the center and movie2 stretches vertically. I’m looking for a similar code that will stretch an image with the browser window, I also need it to remain in the center of the stage. Is this possible using the same or similar style of coding?
thank you for your help
January 10th, 2007 at 8:44 am
Take a look at part II of this tutorial. It covers the basics and should provide the answers to what you’re looking for.
February 6th, 2007 at 9:42 am
[…] Creating Liquid GUIs with Flash – Part 1 Creating Liquid GUIs with Flash – Part 2 Creating Liquid GUIs with Flash – Part 3 […]
February 20th, 2007 at 10:52 am
I am a bit of a noob, and am just about getting to grips with this. I understand the scalable background, and positioning bit, but what if I wanted to have some non-scalable content, like text and other movie clips, but have a background image that fits the browser window?
I tried load movie, but the new movie adopts the characteristics of the parent.
Any help/links really appreciated.
March 6th, 2007 at 9:03 pm
this tutorial is a life saver! however how can you resize or tile a bitmap with this function on a single exit?
March 6th, 2007 at 9:05 pm
ooops…. i meant to say on a single axis….
March 6th, 2007 at 9:22 pm
ok… here is an example of what I am curious about. how is the pattern duplicated on browser resize without distorting the plaid texture?
March 6th, 2007 at 10:00 pm
Caspian: You never want to resize a movieclip with children. Instead, you only resize the children that need resizing. If a movieclip contains 3 children, two of which you want resized and one you don’t. Only resize the two. For example, calling a resizeTo() method on conatiner_mc doesn’t actually alter the height and width of container. Instead it resizes only the children that need it like say background_mc but not message_txt. If background_mc was also a container you would call background_mc.resizeTo() and that method would resize say some of its children and not others. Calling resizeTo() on a movieclip isn’t actually resizing itself, it resizes any children it has that need to be resized.
Jeff: You could create a new blank bitmap the size you want and paint your plaid texture to it N number of times across by N number of rows down. You’ll need to look up the bitmap class to see how this is done. When your movie changes size, call your paintPlaidBG() function again to paint a new background. To speed up rendering during resizes you could instead create one large bitmap plaid and scale a mask over it on resizes.
March 7th, 2007 at 8:31 am
A MASK!!!!!!!!!!!!!
Never thought of that one…. That should be the way to proceed. Good job, you just helped take off some of the project pressure.
Thanks
March 26th, 2007 at 2:01 am
Hi,
I’m making a flash site and it has to stretch to full screen size. You seem to have the answers so could you possibly tell me the best way to do it?
Cheers
May 7th, 2007 at 11:50 am
How would I go about aligning buttons that would align on the upper left of the browser as well as the lower left? Everything I seem to do aligns itself to the top left because of the actionscript aligning it to the top left. Any help would be greatly appreciated. Thanks.
May 7th, 2007 at 11:52 am
ooops… I meant align to the upper right and lower right yet keeping the same layout of having the logo in the upper left. I’m just concerned with my button placement. Sorry about the mix up. This coding has me all messed up. Ha.
June 16th, 2007 at 6:07 am
Hi James,
Thanks for your tutorial. I was wondering if you might be able to help me with something.
I’ve been creating a GUI toolset for use in a few AS3 flash apps I’m building - similar to a very simplified version of .Net’s forms or Java’s swing. So I basically want to create liquid panels with things like flow controls and resizable tabbed controls, etc. That part has proved to be relatively straightforward, but I’m having trouble with something simple.
Basically the simplest GUI elements have formatting similar to CSS with things like ‘margin’ and ‘padding’ etc. So I can space panels accordingly. But I’m having trouble meausuring or setting the correct dimensions of child containers when I subtract margins, etc. from the intended height and width of the child. It’s not my calculations, it happens when I set absoulte values too.
I’m not sure this is clear? What I’m really just wondering, is there something in Flash that you know of that ‘aproximates’ where a vector is actually drawn or where a clip is placed? When I move a panel around, it doesn’t seem to “sit” or “snap” exactly to the pixel coordinate I specify. Is this a known issue? And is it something that can be easily resolved?
The other and perhaps related issue is related to width and height measurements not actually being drawn equally dependant on where the start position is. For example drawing a 600×600 square region begining from 0,0 seems to draw it 600×600 exactly. But when I offset this and change the height to say 599 it seems to draw closer to 598 pixels. The variation is actually quite a lot more than that in some cases.
Am I crazy? It’s doing my head in. I know there are issues with bitmaps and text and other objects not displaying proply if not places on a “whole” pixel. Do you know of any similar issues with Sprite/MovieClip placement or the drawing toolset?
Any help you could give would be greatly appreciated.
Kind regards,
Thomas.
August 28th, 2008 at 6:24 am
Hi, Help please: I haev a big big problem.
The resize works only if I push “resize size of IE or Firefox”…don’t works the first time than I open the webpage!!!
How can I to open automatic resize? Please please please
http://www.openspaces.it/raima/Mediadent/
M
August 28th, 2008 at 6:35 am
Just call your onResize () function manually from within your document class’ constructor. You can pass it a null parameter.
August 28th, 2008 at 7:36 am
I’m so sorry…sincerly I don’t understood…I’m only a art director
Can you explain me please? Thanks so much for your answer.
have a nice day,
M
September 1st, 2008 at 7:39 am
I’d like to create a website (only in flash) which will be show as much the background as the brower’s size. The content (text and photos) won’t be scaled. Something like this: http://www.sevenedge.be/ (btw. the best flash site of the week).
Could you help me, please ?
September 4th, 2008 at 8:31 am
Very nice and thanks for your tutorial.
I think its one of the only tutorial’s` on the web that explained this matter in a very good way.
It’s a bit hard to work with liquid` if you want to combine them with timeline.
For example if you take an object and set it out of the stage (if you want to slide in from out to stage in to stage using the timeline) then you need to set the new object on the liquid again.
Very nice one.
P.S
Just let you know even in Israel we read good stuff (:
January 4th, 2009 at 9:17 am
Your info on the liquid layout is great. Do you have or know of anything that mentions how to have the wrapping of text adjust depending on your browser width/ text column width?
Basically i have a headline and about 3 lines of text, The headline will always be on one line but the lines of text could be anything from 1 - 3 lines (there is a minimum width after which nothing should happen to the swf)
January 4th, 2009 at 10:07 am
All you need to do is set your dynamic textfield to allow multiline wrapping and change the width of the textfield as the browser’s width changes. Flash will automatically re-wrap the text.
January 27th, 2009 at 6:48 am
Thanks for the wonderfull tutorial.
I am using it to create a flash website.
But I have one problem.
In place of images in the middle part I am attaching flipbook components to the clip image_mc.
When the page is laoded it displays everything correctly, but when I resize it the book_mc reduces to very smalll size.
I have tried attching a normal movieclip and it works fine.
Is it the problem with the flipbook component??
Can you help??
Trupti
March 4th, 2009 at 3:30 am
Hy everibody!
I’m looking for a way to set a minimum size for a stage(like 880 x 600, so everitihing fits in, whitout covering eachodther, if grater, then just strech..). Is that posible anyway? Conditionals don’t do nothing, or if they do, they do it anyway, no mater that the conditon is true or false(so far, as I tried, whit not the best methods , i’m a designer).
Or should i try to do it in a nother language? HTML, PHP?
I can’t find anything realted to this question. Hard to belive that nobady is interested. Maybe it’s easy