Creating Collapsible Panel in Flex 4
It is quite common in Flex applications (and other RIAs) to divide screen content into two parts: the navigation/configuration panel on the left and the actual content on the right.
The examples of such apps are FlexStore sample and famous Style Explorer.
I followed this pattern in many projects and several times we come to the point that navigation/configuration panel requires too much space and that there should be a possibility to minimize it. Probably designers of Style Explorer has a similar idea since their navigation control can be minimized. Unfortunately they haven’t created reusable component to do this.
Some time ago I have created the collapsible panel in Flex 3. The idea of this component was to minimize panel by hiding panel content and rotating the header by 90 degree. So that, minimized panel was visible as vertical bar with the title. It was the nightmare to use rotation in Flex 3 since Flex 3 layouts doesn’t care about rotation. Placing component in the right place requires many tricks and extensive usage of trigonometric functions…
When I read that Gumbo (now Flex 4) layouts supports rotation I decided to test it by reimplementing my collapsible panel. Flex 4 definitely passed the test. Spark component architecture is really cool and it is much easier to create reusable/extendible components in Flex 4 than it was before. You can see the result of my work below.
This example is created using 4.0.0.8847, it will not work with SDK shipped with beta 1.
View source is enabled, you can download zipped sources from here.
Following Spark components architecture I have extended Panel class and created new MXML skin.
Component class
Below you can find the list of steps taken to create CollapsiblePanel main component class. At first sight this list may look long and complicated but after understanding Spark architecture it is really simple.
- Adding “collapsed” skin state
[SkinState("collapsed")] public class CollapsiblePanel extends Panel { - Adding new
collapsedfield indicating if panel is collapsed (invalidating skin state in setter)public function get collapsed():Boolean { return _collapsed; } public function set collapsed(value:Boolean):void { _collapsed = value; invalidateSkinState(); } protected var _collapsed:Boolean; - Overriding
getCurrentSkinState()function to support “collapsed” skin stateoverride protected function getCurrentSkinState():String { return collapsed ? "collapsed" : super.getCurrentSkinState(); } - Adding
collapseButtonoptional skin part[SkinPart(required="false")] public var collapseButton:Button;
- Implementing handler to toggle
collapsedproperty oncollapseButtonclickprotected function collapseButtonClickHandler(event:MouseEvent):void { collapsed = !collapsed; } - Overriding
partAdded()/partRemoved()functions to addcollapseButtonevent handleroverride protected function partAdded(partName:String, instance:Object) : void { super.partAdded(partName, instance); if (instance == collapseButton) { Button(instance).addEventListener(MouseEvent.CLICK, collapseButtonClickHandler); } } override protected function partRemoved(partName:String, instance:Object) : void { if (instance == collapseButton) { Button(instance).removeEventListener(MouseEvent.CLICK, collapseButtonClickHandler); } super.partRemoved(partName, instance); }
MXML Skin
The CollapsiblePanelSkin.mxml skin is a copy of default Spark Panel skin with a number of modification. I used enhanced styles syntax to modify skin in “collapsed” state.
- Adding “collapsed” state
<s:states> <s:State name="normal" /> <s:State name="collapsed" /> <s:State name="disabled" /> </s:states> - Grouping all title bar layers inside one Group tag
<s:Group id="titleBarGroup" left="0" top="0" right="0" bottom="0" maxHeight="32" rotation.collapsed="90"> - Adding collapse button
<s:Button id="collapseButton" width="16" height="16" top="7" right="7" label="-" label.collapsed="+" toolTip="Collapse" toolTip.collapsed="Open" /> - Excluding contentGroup from collapsed state
<s:Group id="contentGroup" left="1" right="1" top="32" bottom="1" minWidth="0" minHeight="0" visible.collapsed="false" excludeFrom="collapsed"> </s:Group> - Adding transitions
<s:transitions> <s:Transition fromState="normal" toState="collapsed"> <s:Sequence> <s:Fade target="{contentGroup}" duration="250" /> <s:Parallel targets="{[titleBarGroup, this]}" > <s:Rotate target="{titleBarGroup}" duration="250" /> <s:Resize target="{this}" duration="250" easer="{collapseEaser}" /> </s:Parallel> </s:Sequence> </s:Transition> <s:Transition fromState="collapsed" toState="normal"> <s:Sequence> <s:Parallel targets="{[titleBarGroup, this]}" > <s:Rotate target="{titleBarGroup}" duration="250" /> <s:Resize target="{this}" duration="250" easer="{uncollapseEaser}" /> </s:Parallel> <s:Fade target="{contentGroup}" duration="250" /> </s:Sequence> </s:Transition> </s:transitions>
Summarizing, creating CollapsiblePanel in Flex 4 was much easier than in Flex 3 and the resulting component is of much higher quality. Although understanding Spark components architecture and MXML 2009 may require some time it is definitely worth doing!
Great, thank you.
Hi,
Thanks for great work…but source code is not available ..can u post it again????????
Regards
Dileep
Hi,
I have corrected paths to the sources. You can download it from here. Unfortunately html source view generated by FB4 is incomplete but zipped sources are OK.
Iwo
Thanks for posting these examples !
Hi,
Thanks for sharing collapsible panel. im trying to get a Collapsible Panel in flex 3. can you provide some direction to achieve the same.
thanks
Hi,
nice component. But it doesn’t really work when the panel has an explicit width. It get’s minimized but the border, shadow and background keep their width. I tried to put two of this panels inside a DividedBox. Everything works fine until you resize the box (or give the panel an explicit width). Perhaps you can fix this?
Hi Thomas,
This is rather the demo of Flex 4 potential than the complete component so some other features may not work as well. To fix your problem you can set explicit and percent width to NaN when panel is being collapsed and restore them to previous values when it is un-collapsed. You can do that by adding two “backup variables” and modifying collapsed setter:
public function set collapsed(value:Boolean):void { if (value) { uncollapsedExplicitWidth = explicitWidth; uncollapsedPercentWidth = percentWidth; explicitWidth = NaN; percentWidth = NaN; } else { explicitWidth = uncollapsedExplicitWidth; percentWidth = uncollapsedPercentWidth; } _collapsed = value; invalidateSkinState(); } protected var _collapsed:Boolean; protected var uncollapsedPercentWidth:Number = NaN; protected var uncollapsedExplicitWidth:Number = NaN;If you would like to polish this component I can add it to the reusable-fx google project so that you can share your changes. Give me a note if you are interested.
Hi,
thank’s for your code – I have thought of something in the same way. I’m just playing around with Flex 4 stateful skins and am evaluating to use this kind of panel for the application I am going to build. If I do further modification I’ll give you a note
Hi Karthik,
As you can read above creating collapsible panel in Flex 3 is much harder than in Flex 4 because Flex 3 layouts doesn’t care about rotation. Here are some key concepts regarding my implementation (I can’t share the code since it is copyrighted to my ex-employer):
- CollapsiblePanel extends Canvas and contains standard Panel.
- Panel is placed/rotated inside Canvas using absolute layout (some calculations are necessary to determine correct x/y/width/height during animation.
- addChild, addChildAt etc. functions are overridden to pass child to inner Panel.
- Animation is done using AnimateProperty Prallel and Sequence.
Also remember to use embedded font (other ways all texts disappear after rotation).
All in all, the resulting components code looks quite dirty and is in no way as reusable/extendible as Flex 4 code.
Good luck creating your component! And don’t forget to show us the result of your work
@Iwo Banas
Thanks for the direction. Will work on it and sure will post the code.
think the embedded font will do the trick. i got stuck , when the title text disappeared.
-Karthik
nice work. now i have to play around w/ skinning in Flex4