Creating a new skin class for a ToggleButton - apache-flex

I'm using the following Flex component: http://flashcommander.org/blog/flex-4-mp3-player
The skin that comes with this component only allows for very basic skinning, such as background colour and borders. However the component makes use of a ToggleButton, defined in Mp3Player.as:
import spark.components.ToggleButton;
[SkinPart]
public var playPauseButton:ToggleButton;
What I would like to do is define an alternative skin for this control in the project, I'm pretty new to Flex and Spark so I'm un-sure as to how I go about define a new skin.
In the Mp3PlayerSkin.mxml file the skin is defined as follows:
<!--- #copy spark.components.VideoPlayer#playPauseButton -->
<s:ToggleButton id="playPauseButton" left="0" bottom="0"
skinClass="spark.skins.spark.mediaClasses.normal.PlayPauseButtonSkin"
focusIn="event.target.depth=1" focusOut="event.target.depth=0" />
I assume I can create a new class, although I don't know how to do that, or remove the reference to the class and extend the ToggleButton skin in some way. I'm thinking I need to override the 'default' skin in some way?
Any pointers appreciated ...

Looks like I can do the following:
Create a new Flex Skin by right-clicking the original Mp3PlayerSkin.mxml file
Name the file toggleButtonSkin.mxml
Copy the styles from the spark.components.VideoPlayer#playPauseButton class
Update the Mp3PlayerSkin.mxml as follows:
<s:ToggleButton id="playPauseButton" left="0" bottom="0"
skinClass="org.flashcommander.components.toggleButtonSkin"
focusIn="event.target.depth=1" focusOut="event.target.depth=0" />
Now I can edit the new toggleButtonSkin.mxml file by editing the new styles.

Related

How to understand the "id" attribute of flex skin?

I'm reading some source of flex skin, and found there are some id attributes, which seems important. Take a "button" skin for example:
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
minWidth="21" minHeight="21"
alpha.disabled="0.5">
<!-- host component -->
<fx:Metadata>
<![CDATA[
/**
* #copy spark.skins.spark.ApplicationSkin#hostComponent
*/
[HostComponent("spark.components.Button")]
]]>
</fx:Metadata>
<!-- layer 8: text -->
<s:Group id="textGroup" verticalCenter="1" left="25">
<s:filters>
<s:DropShadowFilter alpha="0.5" blurX="0" blurY="0" distance="1" />
</s:filters>
<s:Label id="labelDisplay"
textAlign="center"
verticalAlign="middle"
maxDisplayedLines="1">
</s:Label>
</s:Group>
</s:SparkSkin>
You can see there are ids of textGroup and labelDisplay. They are important since if I use other ids, the styles won't be applied to button.
But how don I know what ids should I use? Why it's textGroup and labelDisplay? Where can I find the declarations?
I tried to search them in the source of spark.components.Button.as, but not found anything related.
The IDs textGroup and labelDisplay are part of the skinning contract.
If you check the source for ButtonBase the class extended by the Button class you will notice:
[SkinPart(required="false")]
/**
* A skin part that defines the label of the button.
*
* #langversion 3.0
* #playerversion Flash 10
* #playerversion AIR 1.5
* #productversion Flex 4
*/
public var labelDisplay:TextBase;
Here as you can see the declaration tells you that labelDisplay is a TextBase in the skin and that the labelDisplay skin part is technically not required (required="false"), but without it, Flex would not draw a label on the Button control. The default value for the required property is false. The contract between the Button class and its skin dictates that when you set the label property on a button, the value is pushed down into the skin and modifies the value of the labelDisplay skin part’s text, if the labelDisplay skin part exists.
So essentially this means that these IDs will be used in the ButtonBase class. Usually no need to change the Ids unless of course your requirement needs you to do so. In which case you need to change the skinning contract appropriately too.
Check http://help.adobe.com/en_US/flex/using/WSC8DB0C28-F7A6-48ff-9899-7957415A0A49.html
section Skinning contract.
id is used to bind skin part instance in skin and logic in host component.

How to have a cornerradius on Flex 4 TextInput component

How do I get corner radius on my TextInput component in Flex 4.
<s:TextInput prompt="username" width="150" maxChars="100" id="txt_username"
color="#000000"/>
Create a custom skin (possibly by copying spark TextInputSkin) and add a border graphic with rounded corners, like this:
<!-- border -->
<s:Rect id="border" left="0" right="0" top="0" bottom="0"
radiusX="10" radiusY="10">
<s:stroke>
<s:SolidColorStroke id="borderStroke" weight="1" />
</s:stroke>
</s:Rect>
If you want more special rounded corners you can also use these attributes:
topLeftRadiusX="4" topLeftRadiusY="8"
bottomLeftRadiusX="2" bottomRightRadiusY="10"
The default TextInputSkin does not allow for rounded corners, so there is no style that you could set on your TextInput to do it. So, no, there's no other way than creating a custom skin class.
You could take it a step further and make it dynamic. Create a custom TextInputSkin based on spark TextInputSkin and in the updateDisplayList method add this code above the super.updateDisplayList() call.
In YourTextInputSkin.mxml,
// in updateDisplayList()
if (getStyle("cornerRadius")!==undefined) {
border.radiusX = border.radiusY = getStyle("cornerRadius");
background.radiusX = background.radiusY = getStyle("cornerRadius");
}
That's it. You're done!
Now to use it add a CSS class selector to add a cornerRadius style like so:
/* set the Textinput.styleName to this style */
s|TextInput.roundedInput
{
contentBackgroundAlpha: .4;
contentBackgroundColor: #000000;
cornerRadius: 10;
skinClass: ClassReference("view.skins.TextInputRoundedSkin");
}
And set your instance to the class,
<s:TextInput styleName="roundedInput"/>
Unfortunately, you can't set the cornerRadius style on TextInput component instance in MXML. Should Flex support a styles tag for this type of thing like HTML tags do? Should styles declared in the Skin be proxied to the component using them? Currently the Flex compiler would throw an error if you declared a style in the Skin and tried to use it on the component instance even though it's valid to declare that style and any other style in the CSS. What about if UIComponents had a style proxy object that let you set styles? Anyway, I digress.
If you want to make that style available on the TextInput instance in addition to the previous methods you can do that by extending TextInput and adding the cornerRadius style metadata to it. You can also set the skinClass (inline or in a defaults.css file in the library) while you're at it.
Something like this:
<?xml version="1.0" encoding="utf-8"?>
<s:TextInput xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
skinClass="TextInputRoundedSkin" >
<fx:Metadata>
[Style(name="cornerRadius", inherit="no", type="uint")]
</fx:Metadata>
</s:TextInput>
To use,
<local:MyExtendedTextInput cornderRadius="8" />
In the future you won't have to declare the CSS.

A template for button skins using FXG graphics?

The UI in my app uses a lot of buttons that are mostly the same minus the FXG graphics used in their skins. Their skins look something like this
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:s="library://ns.adobe.com/flex/spark" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:ai="http://ns.adobe.com/ai/2009" xmlns:d="http://ns.adobe.com/fxg/2008/dt" xmlns:flm="http://ns.adobe.com/flame/2008" xmlns:graphics="assets.graphics.*" xmlns:ATE="http://ns.adobe.com/ate/2009">
<fx:Metadata>[HostComponent("spark.components.Button")]</fx:Metadata>
<s:states>
<s:State name="up"/>
<s:State name="over"/>
<s:State name="down"/>
<s:State name="disabled"/>
</s:states>
<graphics:RectangleGraphic bottom="0"/>
<graphics:ButtonTextGraphic alpha.disabled="0.45" x="22" y="6"/>
<graphics:ButtonSymbolGraphic alpha.disabled="0.45" x="4" y="4">
<graphics:filters>
<s:GlowFilter includeIn="over, down" blurX="3" blurY="3" inner="false" color="#3F5F93" strength="2" alpha="1.0" quality="2" knockout="false"/>
<s:GlowFilter includeIn="down" blurX="3" blurY="3" inner="false" color="0x5380d0" strength="2" alpha="1.0" quality="2" knockout="false"/>
</graphics:filters>
</graphics:ButtonSymbolGraphic>
<graphics:SmallButtonLineSeparatorGraphic bottom="-0.5"/>
</s:Skin>
where RectangleGraphic and SmallButtonLineSeparatorGraphic are FXGs that are used across all buttons, and ButtonTextGraphic and ButtonSymbolGraphic are FXGs that are specific to each button.
I would love to have a single template skin that is fed the two FXGs specific to each button, but I'm not sure how to best do that.
You'll have to do some ActionScript magic to make it work. Probably Leave RectangleGraphic and SmallButtonLineSeparatorGraphic as they are in MXML. Create variables for ButtonTextGraphic and ButtonSymbolGraphic. I'll just demonstrate using ButtonTextGraphic a sample. Something like this:
public var buttonTextGraphicClass : Class;
Also have variables for the class instance:
public var buttonTextGraphic : Class;
In the constructor, you can set the class values. Or if you're stuck w/ MXML, then use a preinitialize event handler:
buttonTextGraphicClass = ButtonTextGraphic;
in createChildren create the instances and add them:
protected function override createChildren():void{
super.createChildren();
buttonTextGraphic = new buttonTextGraphicClass()
// set other properties
addElement(buttonTextGraphicClass);
}
Finally, in updateDisplayList() size and position them elements, something like this:
buttonTextGraphic.x = 22
buttonTextGraphic.y = 6
buttonTextGraphic.width = unscaledWidth // or some other value
buttonTextGraphic.height = unscaledHeight // or some value
This way you can use the same exactly skin; just replacing the class instances for your changing FXG assets at runtime.
I suggest reading up on the Flex Component LifeCycle. If you have Z-Order issues; you may have to switch to creating all the skin's children in ActionScript; or possibly use the "addElementAt" method.
To accommodate for different states in the ActionScript code, you'll have to override the set currentState method to manually make our state changes, such as changing your glow or changing the alpha.
Pretty much everything I describe here is the approach to creating ActionScript skins. You may benefit from looking at some of the existing Flex 4.5 Mobile Skins. I'd start with the Mobile ButtonSkin. I think the border is an FXG Asset; which is implemented using a similar approach to what I describe here.

How can I add multiple icons to the Spark TabBar control?

In the MX TabBar component, the iconField property allowed us to display different icons in each tab. In Spark, there does not seem to be an inherent way to add icons to the TabBar. Does anyone have an example of implementing icon support for Spark's TabBar? Is there a way to do this without extending the component?
Many thanks!
Hey after spending a week trying to follow multiple ways, (yours being top of the list) i found out a simpler and effective way to add icons to my tab bar, or any other component using skinning.
You dont need to create a custom component, just passing the icon and label through data.
http://cookbooks.adobe.com/post_Tutorials_for_skinning_Spark_ButtonBar_component_w-16722.html
As personally, i was using content navigator with my tabbar/viewstack, i passed the icon as icon instead of imageicon. you can make changes accordingly.
You'll have to create a skin for adding icons to Spark components; it is not as straightforward (IMHO) as Flex 3's MX components, though much more extensible.
Here are a few links which might help you get started:
Tour de Flex Tabbar examples
Custom Skin on Tabbar
Flex Tabbar with Skin
I believe I've come up with a solution, which I'm posting below for posterity. If anyone has a better way, I'd much appreciate the suggestion.
<!-- main app: TabBar implementation -->
<s:TabBar
dataProvider="{contentTabBarPrimaryDP}"
skinClass="skins.ContentTabBarSkin"/>
<!-- skins.ContentTabBarSkin: ItemRenderer implementation -->
<s:DataGroup id="dataGroup" width="100%" height="100%">
<s:layout>
<s:HorizontalLayout/>
</s:layout>
<s:itemRenderer>
<fx:Component>
<custom:IconButtonBarButton
label="{data.label}"
icon="{data.icon}"
skinClass="skins.ContentTabBarButtonSkin"/>
</fx:Component>
</s:itemRenderer>
</s:DataGroup>
<!-- skins.ContentTabBarButtonSkin: icon implementation -->
<s:HGroup
gap="3"
paddingBottom="3"
paddingLeft="3"
paddingRight="3"
paddingTop="3"
verticalAlign="middle">
<!--- layer 2: icon -->
<s:BitmapImage id="iconDisplay"
left="5"
verticalCenter="0" />
<!--- layer 3: label -->
<s:Label id="labelDisplay"
textAlign="center"
verticalAlign="middle"
maxDisplayedLines="1"
horizontalCenter="0" verticalCenter="1"
left="10"
right="10"
top="2"
bottom="2">
</s:Label>
</s:HGroup>
This solution uses a custom DTO object for the TabBar dataProvider which stores the label text as well as the embedded icon image as a class. I also had to extend the ButtonBarButton component to add an iconDisplay SkinPart, which looks like this:
[SkinPart(required="false")]
public var iconDisplay:BitmapImage;
This class also has getters/setters for the icon class property and sets the icon source, as such:
public function set icon(value:Class):void {
_icon = value;
if (iconDisplay != null)
iconDisplay.source = _icon;
}
override protected function partAdded(partName:String, instance:Object):void {
super.partAdded(partName, instance);
if (icon !== null && instance == iconDisplay)
iconDisplay.source = icon;
}
It's seems to be a bug/missed functionality of the your SDK version:
http://forums.adobe.com/thread/552543
http://bugs.adobe.com/jira/browse/SDK-24331
Anyway, thanks for the solution with skins - very helpful

Flex compiler metadata "DefaultProperty"

Given the following:
<?xml version="1.0"?>
<mx:Application xmlns:mx="http://www.adobe.com/2009/mxml">
<mx:Panel title="blah">
<mx:Button />
</mx:Panel>
</mx:Application>
Can you tell me where child elements (ex. mx:Button) are assigned in parent elements (ex. mx:Panel) by default by mxmlc. You can set the "DefaultProperty" compiler metadata tag to specify where they are assigned but what does flex do when it is not specified.
For example I've traversed the source of all the flex classes mx:Panel inherits from and DefaultProperty is never mentioned, leading me to wonder what the default value of DefaultProperty.
sorry for any noobishness but i've read the docs inside out.
When writing AS based components, the default property lets you specify a property that you can use as a child-tag. Eg:
<MyComp:TextAreaDefaultProp>Hello</MyComp:TextAreaDefaultProp>
You could as well have used:
<MyComp:TextAreaDefaultProp defaultText="Hello" />
What happens when you don't specify? You don't get a value for that property. Given the following component:
package
{
// as/myComponents/TextAreaDefaultProp.as
import mx.controls.TextArea;
// Define the default property.
[DefaultProperty("defaultText")]
public class TextAreaDefaultProp extends TextArea {
public function TextAreaDefaultProp()
{
super();
}
// Define a setter method to set the text property
// to the value of the default property.
public function set defaultText(value:String):void {
if (value!=null)
text=value;
}
public function get defaultText():String {
return text;
}
}
}
Run this snippet:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" width="535" height="345"
xmlns:local="*">
<mx:VBox>
<local:TextAreaDefaultProp id="a" defaultText="Hello"/>
<local:TextAreaDefaultProp id="b" > World </local:TextAreaDefaultProp>
<local:TextAreaDefaultProp id="c" />
<mx:TextArea id="z"/>
<mx:Button click="{z.text = a.defaultText
+ ' ' + b.defaultText
+ ' ' + (c.defaultText.length);}" />
</mx:VBox>
</mx:Application>
The compiler actually treats child components of containers as a special case. Take a look at the childDescriptors property of mx.core.Container for some explanation. When you create a Flex component instance in MXML, it isn't instantiated immediately. Instead, a "descriptor" is created, and that is used to instantiate the component at some future time, as determined by the container's creationPolicy property. If you add the -keep-generated-actionscript argument (or the shortened version, -keep) to your compiler arguments, you'll be able to see the AS3 code that the compiler generates from MXML.

Resources