Search code examples
apache-flexactionscriptdrawingskinbuttonbar

Flex 4 - How to draw a shape to use as a skin for a ButtonBarButton


I'm using Flash Builder 4 and am in the process of learning.

The short and simple explanation is, I want a tab navigator that has tabs that look like this:

First TabMiddle Tabs

I know I could do this using an image-based skin, but I figured (and could be wrong) that programmatically drawing the shape would be better in terms of scalability.

As long as I get the result I'm looking for, I guess I don't really care if it's mx or spark. I tried doing this:

Main App:

<mx:ButtonBar dataProvider="{vsTabNav}" firstButtonStyle="firstButtonStyle"/>

CSS File:

.firstButtonStyle { skinClass:ClassReference("assets.skins.ButtonBarFirstButtonSkin"); }

ButtonBarFirstButtonSkin.as:

package assets.skins
{
    import flash.display.Graphics;
    import mx.skins.halo.ButtonBarButtonSkin;
    import mx.graphics.RectangularDropShadow;

    public class ButtonBarFirstButtonSkin extends ButtonBarButtonSkin
    {
        private var dropShadow:RectangularDropShadow;

        public function ButtonBarFirstButtonSkin()
        {
            super();
        }

        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void 
        {
            super.updateDisplayList(unscaledWidth, unscaledHeight);

            var cornerRadius:Number = getStyle("cornerRadius");
            var backgroundColor:int = getStyle("backgroundColor");
            var backgroundAlpha:Number = getStyle("backgroundAlpha");
            graphics.clear();

            cornerRadius = 10;
            backgroundColor = 0xFF0000;
            backgroundAlpha = 1;

            // Background
            drawRoundRect(0, 0, unscaledWidth, unscaledHeight, {tl:1, tr:cornerRadius, bl:1, br:1}, backgroundColor, backgroundAlpha);

            // Shadow
            if (!dropShadow)
                dropShadow = new RectangularDropShadow();

            dropShadow.distance = 8;
            dropShadow.angle = 45;
            dropShadow.color = 0;
            dropShadow.alpha = 0.4;
            dropShadow.tlRadius = 1;
            dropShadow.trRadius = cornerRadius;
            dropShadow.blRadius = 1;
            dropShadow.brRadius = 1;
            dropShadow.drawShadow(graphics, 0, 0, unscaledWidth, unscaledHeight);
        }

    }
}

This should mean that the first button will be red and will have a very round top-right corner. Instead, I just get the default button. Not sure what I'm doing wrong there, but if that's not the best solution, I would love some help. Thanks!


Solution

  • First, have a look at http://www.adobe.com/devnet/flex/articles/flex4_skinning.html

    Then, you should realize, you'll be better off creating first skins for your ButtonBarButtons and make sure your button bar uses them for its buttons.

    Also, you can create the shapes with the Path class. Following is an example that creates similar shapes to yours:

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
                   xmlns:s="library://ns.adobe.com/flex/spark" 
                   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
        <s:Path x="10" y="10"
                data="M 0 2 V 18 H 200 Q 190 3 170 0 H 2 L 0 2 Z" 
                width="200" height="20"  >
            <s:fill>
                <s:LinearGradient rotation="90">
                    <s:GradientEntry color="0xFFFFFF" />
                    <s:GradientEntry color="0xFDFDFD" ratio="0.6" />
                    <s:GradientEntry color="0x8A8A8A" ratio="1" />
                </s:LinearGradient>
            </s:fill>
            <s:stroke>
                <s:SolidColorStroke color="0x000000" />
            </s:stroke>
        </s:Path>
    
        <s:Path x="215" y="10"
                data="M 30 0 Q 10 0 0 20 H 200 Q 190 3 170 0 H 30 Z" 
                width="200" height="20"  >
            <s:fill>
                <s:LinearGradient rotation="90">
                    <s:GradientEntry color="0x8f8f8f" />
                    <s:GradientEntry color="0x878787" ratio="0.6" />
                    <s:GradientEntry color="0x5d5d5d" ratio="1" />
                </s:LinearGradient>
            </s:fill>
            <s:stroke>
                <s:SolidColorStroke color="0x000000" />
            </s:stroke>
        </s:Path>
    </s:Application>
    

    UPDATE: If you want scaling, you can put the Path element into a Graphic element, and setup its scaleGrid properties:

    <s:Graphic scaleGridTop="1" scaleGridBottom="19" scaleGridLeft="10" scaleGridRight="170"
               width="150" height="15"
               x="10" y="10" 
               >
        <s:Path 
                data="M 0 2 V 18 H 200 Q 190 3 170 0 H 2 L 0 2 Z" 
                width="200" height="20" >
            <s:fill>
                <s:LinearGradient rotation="90">
                    <s:GradientEntry color="0xFFFFFF" />
                    <s:GradientEntry color="0xFDFDFD" ratio="0.6" />
                    <s:GradientEntry color="0x8A8A8A" ratio="1" />
                </s:LinearGradient>
            </s:fill>
            <s:stroke>
                <s:SolidColorStroke color="0x000000" />
            </s:stroke>
        </s:Path>
    </s:Graphic>