AIR Mobile :: Hello Callout Style & Skin
The Callout container pops up above application content when initiated by some type of user interaction or programmatic trigger. The Callout is a subclass of the SkinnablePopUpContainer which implements the open() and close() aspects of the Callout container.
The Callout can be modal open(owner, true) or non modal open(owner, false).
What Is The Callout
The Callout is a SkinnablePopUpContainer that implements custom logic for the following;
- The callout’s
arrowDirection–ArrowDirection.RIGHT,ArrowDirection.UP,ArrowDirection.LEFT,ArrowDirection.DOWN,ArrowDirection.NONE
Styling The Callout
contentBackgroundAppearance
The contentBackgroundAppearance style of the Callout allows the visual appearance of the contentGroup to be framed by an inset, flat or none border.
NOTE: The red Rect background is to show the bounds of the contentGroup. Use the backgroundColor and backgroundAlpha to actually set the color of the background, you don’t add the Rect like I have below, it’s for explanation only.
inset

Note the beveled white border and top inset shadow on top of the contentGroup.
flat

Note the bevel is gone and the contentGroup is still masked by the cornerRadius of the Callout skin.
none

Note the inset and mask is not present, the contentGroup is displayed without any border frame.
Skinning The Callout
The Callout component uses the CalloutSkin class to create it’s skin parts based off of the MobileSkin. Skins based off the MobileSkin are a bit more complicated for new developers to wrap their heads around since they don’t follow the Spark MXML pattern for creating skins. The Callout skin is adjustable by creating a subclass and setting protected property values.
Protected Properties that can be adjusted
dropShadowVisible:Boolean– Whether the drop shadow of theCalloutis visible.useBackgroundGradient:Boolean– Whether theCalloutadjusts the background gradient by a 15% lighten and 60% darken algorithm.- This brightness adjustment affects the
backgroundColorstyle when set.
- This brightness adjustment affects the
contentCornerRadius:uint– Adjusts the corner radius of thecontentBackgroundColorstyle mask mentioned above.- This property only applies to the inset and flat
contentBackgroundAppearancestyle value.
- This property only applies to the inset and flat
contentBackgroundInsetClass:Class– TheClassused to render the inset and drop shadow show in the inset image above.- Default –
spark.skins.mobile*.assets.CalloutContentBackground– this is an FXG asset.
- Default –
backgroundCornerRadius:Number– The corner radius of thebackgroundColorstyle fill.frameThickness:Number– The thickness of thebackgroundColorframe.- This is comparable to the
contentGroup'spadding from the edge of theCallout.
- This is comparable to the
borderColor:Number– The stroke color of thebackgroundColorfill.borderThickness:Number– The thickness of thebackgroundColorborder frame.arrowWidth:Number– The width of the arrow skin part in the vertical direction and the height of the arrow skin part in the horizontal direction.arrowHeight:Number– The height of the arrow skin part in the horizontal direction and the width of the arrow skin part in the vertical direction.
A simple MyCalloutSkin override
In the code below, the non commented lines are the protected variables of the subclass that are affecting the MyCalloutSkin skin.
MXML
<s:Callout> <s:Rect width="100%" height="100%"> <s:fill> <s:SolidColor color="#FF0000"/> </s:fill> </s:Rect> <s:VGroup paddingTop="5" paddingRight="5" paddingBottom="5" paddingLeft="5"> <s:Label text="contentGroup"/> </s:VGroup> </s:Callout>
CSS
s|Callout { contentBackgroundAppearance:none; skinClass:ClassReference("skins.MyCalloutSkin"); }
skins.MyCalloutSkin
package skins { import spark.skins.mobile.CalloutSkin; public class MyCalloutSkin extends CalloutSkin { public function MyCalloutSkin() { super(); // assuming a 160 DPI for simplicity dropShadowVisible = false; useBackgroundGradient = false; //contentCornerRadius = 0; //contentBackgroundInsetClass = null; //backgroundCornerRadius = 20; frameThickness = 50; borderColor = 0xFFCC00; borderThickness = 10; arrowHeight = 15; //arrowWidth = 10; } } }

A simple MyCalloutSkin override of inset
In the code below, the non commented lines are the protected variables of the subclass that are affecting the MyCalloutSkin skin with an inset contentBackgroundAppearance style.
CSS
s|Callout { contentBackgroundAppearance:inset; skinClass:ClassReference("skins.MyCalloutSkin"); }
skins.MyCalloutSkin
package skins { import spark.skins.mobile.CalloutSkin; public class MyCalloutSkin extends CalloutSkin { public function MyCalloutSkin() { super(); // assuming a 160 DPI for simplicity dropShadowVisible = false; useBackgroundGradient = true; //contentCornerRadius = 0; //contentBackgroundInsetClass = null; backgroundCornerRadius = 0; frameThickness = 50; borderColor = 0xFFCC00; borderThickness = 5; arrowHeight = 30; //arrowWidth = 10; } } }

Note:
- The
useBackgroundGradientproperty is nowtrue. - The
backgroundCornerRadiusproperty is set to 0. - The
borderThicknessproperty is now set to 5. - The
arrowHeightproperty is now set to 30.
You will also notice that the contentGroup has an inset border above it with a white border. This is the FXG asset mentioned above. The contentCornerRadius property relates to the actual corner radius used IN the FXG asset. The contentCornerRadius property matches the backgroundColor style’s inset mask to the DPI dependent FXG asset’s corner radius so the skin correctly masks the FXG inset resolution used.
Skinning the contentBackgroundInsetClass
You may be wondering if the FXG inset graphic is skinnable and the answer is yes. If you are new to skinning or FXG you would not be aware that FXG is compiled to native graphics and becomes a SpriteVisualElement during runtime. This means that any SpriteVisualElement can replace the default contentBackgroundInsetClass. Also note that if you choose to replace this class with your own implementation, you will need to supply DPI dependent versions if you plan on creating a DPI independent application.
CalloutInset.fxg
<Graphic version="2.0" xmlns="http://ns.adobe.com/fxg/2008" scaleGridLeft="6" scaleGridRight="294" scaleGridTop="15" scaleGridBottom="194"> <!-- invisible fix for scaling --> <Rect x="0" y="199" width="300" height="1"> <fill> <SolidColor color="#ffffff" alpha="0"/> </fill> </Rect> <!-- Content Shading Top --> <Rect x="0" y="0" width="300" height="10" topLeftRadiusX="0" topLeftRadiusY="0" topRightRadiusX="0" topRightRadiusY="0"> <fill> <LinearGradient rotation="90"> <GradientEntry ratio="0" color="#000000" alpha="0.6"/> <GradientEntry ratio="0.5" color="#000000" alpha="0"/> </LinearGradient> </fill> </Rect> <!-- Content Highlight --> <Rect x="0.5" y="0.5" width="299" height="199" radiusX="0" radiusY="0"> <stroke> <SolidColorStroke color="#FFCC00" alpha="0.8" weight="2"/> </stroke> </Rect> </Graphic>
skins.MyCalloutSkin
package skins { import spark.skins.mobile.CalloutSkin; public class MyCalloutSkin extends CalloutSkin { public function MyCalloutSkin() { super(); // assuming a 160 DPI for simplicity dropShadowVisible = false; useBackgroundGradient = true; contentCornerRadius = 0; contentBackgroundInsetClass = CalloutInset; backgroundCornerRadius = 0; frameThickness = 50; borderColor = 0xFFCC00; borderThickness = 5; arrowHeight = 30; //arrowWidth = 10; } } }

Note that we now use the contentCornerRadius property to match up with the FXG radii which are now 0. The contentBackgroundInsetClass property is set to CalloutInset and the inset will now be used instead of the default defined in CalloutSkin.
Using a PNG background skin
Although the example below is rough and does not use 9 scale slicing, it does show that creating Bitmap backgrounds for the Callout is pretty simple based on what you want.
To get proper scaling and 9 scale, there is a little more work involved not covered in this example. Also, there might be logic that determines what png to use based on the arrow layout.
skins.MyCalloutSkin
package skins { import assets.CalloutInset; import flash.display.DisplayObject; import spark.skins.mobile.CalloutSkin; public class MyCalloutSkin extends CalloutSkin { [Embed(source="/assets/callout-background.png", scaleGridTop="44", scaleGridRight="85", scaleGridBottom="47", scaleGridLeft="16")] private var backgroundClass:Class; private var background:Object; public function MyCalloutSkin() { super(); // assuming a 160 DPI for simplicity dropShadowVisible = false; useBackgroundGradient = true; contentCornerRadius = 0; contentBackgroundInsetClass = CalloutInset; backgroundCornerRadius = 0; frameThickness = 10; borderColor = 0xFFCC00; borderThickness = 0; //arrowHeight = 30; //arrowWidth = 10; } override protected function createChildren():void { // put the background behind everything background = new backgroundClass(); addChild(DisplayObject(background)); super.createChildren(); } override protected function drawBackground(unscaledWidth:Number, unscaledHeight:Number):void { // create a simple png background using the superclass's layout frame logic var showBorder:Boolean = !isNaN(borderThickness); var borderWeight:Number = showBorder ? borderThickness : 0; // calculate the frame which the Bitmap will be positioned and sized to var frameX:Number = Math.floor(contentGroup.getLayoutBoundsX() - frameThickness) - (borderWeight / 2); var frameY:Number = Math.floor(contentGroup.getLayoutBoundsY() - frameThickness) - (borderWeight / 2); var frameWidth:Number = contentGroup.getLayoutBoundsWidth() + (frameThickness * 2) + borderWeight; var frameHeight:Number = contentGroup.getLayoutBoundsHeight() + (frameThickness * 2) + borderWeight; background.x = frameX; background.y = frameY; background.width = frameWidth background.height = frameHeight; } } }

As you can see the above image square is the callout-background.png and the Bitmap is sized and placed behind all layout assets.
Conclusion
The Callout component it quite easy to style basic. The styling gets more invloved when you want more dynamic styling than the inset background or any type of background that is not a gradient or stroked fill. I might actually set out to make a skin that is a little more user friendly for those that are looking to style with abit more CSS and are not to concerned about the impact of their application, for example maybe a tablet application.
Part 2 will focus more on the CalloutArrow placements, skinning and the CalloutButton implementation.
About the Author
Michael Schmalle has been developing Adobe Flex and the Flash Player since Flash version 5 (2002). He has numerous open source projects in ActionScript3 (including an ActionScript3 parser/lexer DOM for reading and writing AS3 source code) and Flex (user interface components). He has also been an active participant in the Flex community (just goolge him or Teoti Graphix, LLC) since 2004. He also realizes technologies change and is actively developing user interface components and applications on the Google Android mobile platform.
See Teoti Graphix. LLC.
GIT: https://github.com/teotigraphix
Twitter: http://twitter.com/TeotiGraphix/




Recent Comments