Setting the alpha on a Spark Panel container title bar in Flex 4

The following example shows how you can create a Spark Panel container with a semi-transparent title bar in Flex 4 by creating a custom Spark Panel skin and setting the alpha property on the tbFill, tbHilite, and tbDiv skin parts, and then setting the backgroundAlpha style on the Panel container.

The following example(s) require Flash Player 10 and the Adobe Flex 4 SDK. To download the Adobe Flash Builder 4 trial, see www.adobe.com/products/flex/. To download the latest nightly build of the Flex 4 SDK, see opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4.

For more information on getting started with Flex 4 and Flash Builder 4, see the official Adobe Flex Team blog.

The following example has three Spark Panel containers. The first Panel uses the default skin and setting the backgroundAlpha style only sets the alpha on the Panel background and not the title bar. The second Panel sets the skinClass style to our custom Spark Panel skin, and setting the backgroundAlpha style sets the alpha on the Panel background AND the title bar area. The third Panel also sets the skinClass style to our custom Spark Panel skin but also uses a Spark BorderContainer container to override the background fill on the Panel so only the Panel title bar area changes alpha.

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2010/04/03/setting-the-alpha-on-a-spark-panel-container-title-bar-in-flex-4/ -->
<s:Application name="Spark_Panel_skin_tbFill_alpha_test"
        xmlns:fx="http://ns.adobe.com/mxml/2009" 
        xmlns:s="library://ns.adobe.com/flex/spark" 
        xmlns:mx="library://ns.adobe.com/flex/mx">
    <s:controlBarContent>
        <mx:Form>
            <mx:FormItem label="backgroundAlpha:">
                <s:HSlider id="sldr"
                        minimum="0.0" maximum="1.0"
                        value="1.0"
                        snapInterval="0.1" stepSize="0.1" />
            </mx:FormItem>
            <mx:FormItem label="backgroundColor:">
                <mx:ColorPicker id="clrPckr"
                        selectedColor="white" />
            </mx:FormItem>
        </mx:Form>
    </s:controlBarContent>
 
    <!-- background fill -->
    <s:Rect width="100%" height="100%">
        <s:fill>
            <s:BitmapFill source="@Embed('skins/pattern_149.gif')" fillMode="repeat" />
        </s:fill>
    </s:Rect>
 
    <s:HGroup horizontalCenter="0" verticalCenter="-0">
        <s:Panel id="pnl1"
                 title="Default Spark Panel"
                 backgroundAlpha="{sldr.value}"
                 backgroundColor="{clrPckr.selectedColor}"
                 width="200" height="160">
            <s:Scroller left="0" right="0" top="0" bottom="0">
                <s:VGroup width="100%" height="100%" paddingLeft="20" paddingRight="20" paddingTop="20" paddingBottom="20">
                    <s:Label text="Spark Panel contents" width="100%" />
                </s:VGroup>
            </s:Scroller>
        </s:Panel>
 
        <s:Panel id="pnl2"
                title="Spark Panel w/ alpha"
                skinClass="skins.CustomPanelSkin"
                backgroundAlpha="{sldr.value}"
                backgroundColor="{clrPckr.selectedColor}"
                width="200" height="160">
            <s:Scroller left="0" right="0" top="0" bottom="0">
                <s:VGroup width="100%" height="100%" paddingLeft="20" paddingRight="20" paddingTop="20" paddingBottom="20">
                    <s:Label text="Spark Panel contents" width="100%" />
                </s:VGroup>
            </s:Scroller>
        </s:Panel>
 
        <s:Panel id="pnl3"
                 title="Spark Panel w/ alpha+fill"
                 skinClass="skins.CustomPanelSkin"
                 backgroundAlpha="{sldr.value}"
                 backgroundColor="{clrPckr.selectedColor}"
                 width="200" height="160">
            <s:BorderContainer backgroundColor="white" borderVisible="false" width="100%" height="100%">
                <s:Scroller left="0" right="0" top="0" bottom="0">
                    <s:VGroup width="100%" height="100%" paddingLeft="20" paddingRight="20" paddingTop="20" paddingBottom="20">
                        <s:Label text="Spark Panel contents" width="100%" />
                    </s:VGroup>
                </s:Scroller>
            </s:BorderContainer>
        </s:Panel>
    </s:HGroup>
 
</s:Application>

And the custom Spark Panel skin, skins/CustomPanelSkin.mxml, is as follows:

<?xml version="1.0" encoding="utf-8"?>
<!-- http://blog.flexexamples.com/2010/04/03/setting-the-alpha-on-a-spark-panel-container-title-bar-in-flex-4/ -->
<s:SparkSkin name="CustomPanelSkin"
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
        blendMode="normal"
        mouseEnabled="false"
        minWidth="131" minHeight="127"
        alpha.disabled="0.5" alpha.disabledWithControlBar="0.5">
    <!-- states -->
    <s:states>
        <s:State name="normal" />
        <s:State name="disabled" />
        <s:State name="normalWithControlBar" stateGroups="withControls" />
        <s:State name="disabledWithControlBar" stateGroups="withControls" />
    </s:states>
 
    <fx:Metadata>
        [HostComponent("spark.components.Panel")]
    </fx:Metadata>
 
    <fx:Script fb:purpose="styling">
        <![CDATA[
            /* Define the skin elements that should not be colorized.
            For panel, border and title background are skinned, but the content area and title text are not. */
            static private const exclusions:Array = ["background", "titleDisplay", "contentGroup", "controlBarGroup"];
 
            override public function get colorizeExclusions():Array {
                return exclusions;
            }
 
            override protected function initializationComplete():void {
                useChromeColor = true;
                super.initializationComplete();
            }
 
            override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
                if (getStyle("borderVisible") == true) {
                    border.visible = true;
                    background.left = background.top = background.right = background.bottom = 1;
                    contents.left = contents.top = contents.right = contents.bottom = 1;
                } else {
                    border.visible = false;
                    background.left = background.top = background.right = background.bottom = 0;
                    contents.left = contents.top = contents.right = contents.bottom = 0;
                }
 
                dropShadow.visible = getStyle("dropShadowVisible");
 
                var cr:Number = getStyle("cornerRadius");
                var withControls:Boolean = (currentState == "disabledWithControlBar" || currentState == "normalWithControlBar");
 
                if (cornerRadius != cr) {
                    cornerRadius = cr;
 
                    dropShadow.tlRadius = cornerRadius;
                    dropShadow.trRadius = cornerRadius;
                    dropShadow.blRadius = withControls ? cornerRadius : 0;
                    dropShadow.brRadius = withControls ? cornerRadius : 0;
 
                    setPartCornerRadii(topMaskRect, withControls);
                    setPartCornerRadii(border, withControls);
                    setPartCornerRadii(background, withControls);
                }
 
                if (bottomMaskRect) {
                    setPartCornerRadii(bottomMaskRect, withControls);
                }
 
                borderStroke.color = getStyle("borderColor");
                borderStroke.alpha = getStyle("borderAlpha");
                backgroundFill.color = getStyle("backgroundColor");
                backgroundFill.alpha = getStyle("backgroundAlpha");
 
                super.updateDisplayList(unscaledWidth, unscaledHeight);
            }
 
            private function setPartCornerRadii(target:Rect, includeBottom:Boolean):void {
                target.topLeftRadiusX = cornerRadius;
                target.topRightRadiusX = cornerRadius;
                target.bottomLeftRadiusX = includeBottom ? cornerRadius : 0;
                target.bottomRightRadiusX = includeBottom ? cornerRadius : 0;
            }
 
            private var cornerRadius:Number;
        ]]>
    </fx:Script>
 
    <!-- drop shadow can't be hittable so it stays sibling of other graphics -->
    <s:RectangularDropShadow id="dropShadow"
            blurX="20" blurY="20" alpha="0.32" distance="11"
            angle="90" color="#000000"
            left="0" top="0" right="0" bottom="0"/>
 
    <!-- drop shadow can't be hittable so all other graphics go in this group -->
    <s:Group left="0" right="0" top="0" bottom="0">
 
        <!-- top group mask -->
        <s:Group id="topGroupMask"
                 left="1" top="1" right="1" bottom="1">
            <s:Rect id="topMaskRect"
                    left="0" top="0" right="0" bottom="0">
                <s:fill>
                    <s:SolidColor alpha="0"/>
                </s:fill>
            </s:Rect>
        </s:Group>
 
        <!-- bottom group mask -->
        <s:Group id="bottomGroupMask"
                 left="1" top="1" right="1" bottom="1"
                 includeIn="normalWithControlBar, disabledWithControlBar">
            <s:Rect id="bottomMaskRect" left="0" top="0" right="0" bottom="0">
                <s:fill>
                    <s:SolidColor alpha="0"/>
                </s:fill>
            </s:Rect>
        </s:Group>
 
        <!-- layer 1: border -->
        <s:Rect id="border"
                left="0" right="0" top="0" bottom="0" >
            <s:stroke>
                <s:SolidColorStroke id="borderStroke" weight="1" />
            </s:stroke>
        </s:Rect>
 
        <!-- layer 2: background fill -->
        <!--- Defines the appearance of the PanelSkin class's background. -->
        <s:Rect id="background"
                left="1" top="1" right="1" bottom="1">
            <s:fill>
                <s:SolidColor id="backgroundFill" color="#FFFFFF"/>
            </s:fill>
        </s:Rect>
 
        <!-- layer 3: contents -->
        <!--- Contains the vertical stack of titlebar content and controlbar. -->
        <s:Group id="contents"
                 left="1" right="1" top="1" bottom="1">
            <s:layout>
                <s:VerticalLayout gap="0" horizontalAlign="justify" />
            </s:layout>
 
            <s:Group id="topGroup" mask="{topGroupMask}">
 
                <!-- layer 0: title bar fill -->
                <s:Rect id="tbFill"
                        left="0" right="0" top="0" bottom="1"
                        alpha="0.2">
                    <s:fill>
                        <s:LinearGradient rotation="90">
                            <s:GradientEntry color="0xE2E2E2" />
                            <s:GradientEntry color="0xD9D9D9" />
                        </s:LinearGradient>
                    </s:fill>
                </s:Rect>
 
                <!-- layer 1: title bar highlight -->
                <s:Rect id="tbHilite"
                        left="0" right="0" top="0" bottom="0"
                        alpha="0.2">
                    <s:stroke>
                        <s:LinearGradientStroke rotation="90" weight="1">
                            <s:GradientEntry color="0xEAEAEA" />
                            <s:GradientEntry color="0xD9D9D9" />
                        </s:LinearGradientStroke>
                    </s:stroke>
                </s:Rect>
 
                <!-- layer 2: title bar divider -->
                <s:Rect id="tbDiv"
                        left="0" right="0" height="1" bottom="0"
                        alpha="0.2">
                    <s:fill>
                        <s:SolidColor color="0xC0C0C0" />
                    </s:fill>
                </s:Rect>
 
                <!-- layer 3: text -->
                <s:Label id="titleDisplay"
                         maxDisplayedLines="1"
                         left="9" right="3" top="1" bottom="0"
                         minHeight="30"
                         verticalAlign="middle" fontWeight="bold">
                </s:Label>
            </s:Group>
 
            <!--
            Note: setting the minimum size to 0 here so that changes to the host component's
            size will not be thwarted by this skin part's minimum size.   This is a compromise,
            more about it here: http://bugs.adobe.com/jira/browse/SDK-21143
            -->
            <s:Group id="contentGroup"
                     width="100%" height="100%"
                     minWidth="0" minHeight="0">
            </s:Group>
 
            <s:Group id="bottomGroup"
                     minWidth="0" minHeight="0"
                     includeIn="normalWithControlBar, disabledWithControlBar" >
 
                <s:Group left="0" right="0" top="0" bottom="0" mask="{bottomGroupMask}">
 
                    <!-- layer 0: control bar divider line -->
                    <s:Rect left="0" right="0" top="0" height="1" alpha="0.22">
                        <s:fill>
                            <s:SolidColor color="0x000000" />
                        </s:fill>
                    </s:Rect>
 
                    <!-- layer 1: control bar highlight -->
                    <s:Rect left="0" right="0" top="1" bottom="0">
                        <s:stroke>
                            <s:LinearGradientStroke rotation="90" weight="1">
                                <s:GradientEntry color="0xE5E5E5" />
                                <s:GradientEntry color="0xD8D8D8" />
                            </s:LinearGradientStroke>
                        </s:stroke>
                    </s:Rect>
 
                    <!-- layer 2: control bar fill -->
                    <s:Rect left="1" right="1" top="2" bottom="1">
                        <s:fill>
                            <s:LinearGradient rotation="90">
                                <s:GradientEntry color="0xDADADA" />
                                <s:GradientEntry color="0xC5C5C5" />
                            </s:LinearGradient>
                        </s:fill>
                    </s:Rect>
                </s:Group>
                <!-- layer 3: control bar -->
                <s:Group id="controlBarGroup"
                         left="0" right="0" top="1" bottom="1" minWidth="0" minHeight="0">
                    <s:layout>
                        <s:HorizontalLayout paddingLeft="10" paddingRight="10" paddingTop="7" paddingBottom="7" gap="10" />
                    </s:layout>
                </s:Group>
            </s:Group>
        </s:Group>
    </s:Group>
 
</s:SparkSkin>

This entry is based on a beta version of the Flex 4 SDK and therefore is very likely to change as development of the Flex SDK continues. The API can (and will) change causing examples to possibly not compile in newer versions of the Flex 4 SDK.

3 thoughts on “Setting the alpha on a Spark Panel container title bar in Flex 4

  1. Hi. I compared the code in the skin you provided, with the code Flash Builder 4 writes when creating a new skin for Panel, and saw that the differences are the ‘ alpha=”0.2″ ‘ you added to the three Rect tags in the topGroup. I don’t understand why if you add that property setting to the tag, then you are allowed to change the property value later, and if you don’t add it, its default value becomes unchangeable (or at least, moving the slider has no effect). Could you explain why?
    I’m new to Flex, sorry if my question has an obvious answer.
    Thank you very much.

  2. Ok, I got it. When you modify the skin class, you make the titlebar’s background transparent, so you can see the panel’s background; after that, you make the panel’s background transparent via the backgroundAlpha property, and then you can see what’s behind the panel.
    Obvious answer ;)

Comments are closed.