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

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.

Related

Creating a new skin class for a ToggleButton

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.

Where to put layout code

I need some code to run in a component after all the sizes of its children are known. The component has an absolute layout. I thought my component's measure() function would get called after all it's children's measure() functions had been called, but it seems like since it has an absolute layout, it never even calls measure.
Anyways, it's a Canvas and my override protected function measure():void never runs. What am I doing wrong?
updateDisplayList() is called in UIComponent's commitProperties. I believe this is called after the child sizes are known, and it is where you are supposed to put your layout code.
measure() is not called when you explicitly set a width and height on your components. If you have an absolute layout on your Canvas, but do not set a width and height, measure() will be called.
HTH;
Amy
You don't need to override anything, simply add a listener to the creationComplete of your component. This event is "Dispatched when the component has finished its construction, property processing, measuring, layout, and drawing."
Example where the component inherits from s:Group
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="300"
creationComplete="group1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
protected function group1_creationCompleteHandler(event:FlexEvent):void
{
//TODO add code here
}
]]>
</fx:Script>
</s:Group>

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

Resources