(another of my never ending questions...)
I need an image component with states which behaves like a ToggleButton. Rather than build from scratch I thought I would try subclassing ToggleButton and defining a custom Flex skin. I don't have much experience with skinning and have cobbled together the code below.
It seems to work well – binding BitmapImages in the skin to BitmapData loading in the button instance: <s:BitmapImage source="{hostComponent.upImageData}"
Am I on the right track with this approach? Is there a more standard approach to this sort of thing?
ToggleButton Skin
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
minWidth="21" minHeight="21"
alpha.disabledStates="0.5">
<fx:Metadata>[HostComponent("components.EventButton")]</fx:Metadata>
<!-- host component -->
<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" stateGroups="overStates" />
<s:State name="down" stateGroups="downStates" />
<s:State name="disabled" stateGroups="disabledStates" />
<s:State name="upAndSelected" stateGroups="selectedStates, selectedUpStates" />
<s:State name="overAndSelected" stateGroups="overStates, selectedStates" />
<s:State name="downAndSelected" stateGroups="downStates, selectedStates" />
<s:State name="disabledAndSelected" stateGroups="selectedUpStates, disabledStates, selectedStates" />
</s:states>
<s:BitmapImage source="{hostComponent.upImageData}"
includeIn="up, disabled"
left="0" right="0" top="0" bottom="0" />
<s:BitmapImage source="{hostComponent.overImageData}"
left="0" right="0" top="0" bottom="0"
includeIn="over"/>
<s:BitmapImage source="{hostComponent.downImageData}"
left="0" right="0" top="0" bottom="0"
includeIn="down, upAndSelected, overAndSelected, downAndSelected"/>
</s:Skin>
ToggleButton
<?xml version="1.0" encoding="utf-8"?>
<s:ToggleButton xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:skins = "skins.*"
skinClass="skins.EventSkin"
creationComplete="creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
[Bindable]
public var upImageData:BitmapData;
[Bindable]
public var overImageData:BitmapData;
[Bindable]
public var downImageData:BitmapData;
[Bindable]
public var disabledImageData:BitmapData;
// image loading code removed
]]>
</fx:Script>
</s:ToggleButton>
Yep, you are going in right way. The "Skin mechanism" is really powerful and flexible tool. I had similar task about year ago and I solved it in the same way (just with standard ToggleButton and custom skin). All changes in visual representation of spark component might to be in skin class.
As another option, you should be able to use the state variable syntax:
<s:ToggleButton icon.up="{upIcon}"
icon.over="{overIcon}"
icon.down="{downIcon}"
icon.disabled="{disabledIcon}"
icon.upAndSelected="{upAndSelectedIcon}"
icon.overAndSelected="{overAndSelectedIcon}"
icon.downAndSelected="{downAndSelectedIcon}"
icon.disabledAndSelected="{disabledAndSelectedIcon}" />
See this Apache mailing list thread for more discussion on this.
Related
I'm trying to create a Panel skin (Spark) with a background fill color I can pass as a parameter.
(See in bold:)
<?xml version="1.0" encoding="utf-8"?>
<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">
<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>
<s:Group left="0" right="0" top="0" bottom="0">
<s:Rect left="0" right="0" top="0" bottom="0" radiusX="12" radiusY="12">
<s:fill>
<s:SolidColor color="#184c81" />
</s:fill>
</s:Rect>
<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="contentGroup" width="100%" height="100%" minWidth="0" minHeight="0">
</s:Group>
</s:Group>
</s:Group>
</s:SparkSkin>
From what I read I should create a subclass, but there is not much material on the subject.
I would later on want to use this skin in many Panel controls I have in my application.
Base Panel skin has background color implementation using css styles. You can set it using action script:
panel.setStyle("backgroundColor", 0x184c81);
Also you can control skin components when you have a reference to it in the host component.
Read Implementing the partAdded() and partRemoved() methods for skinnable components for Spark components
This is simple.Create a panel skin and name it something as "panelSkin.mxml".In the .as file use the following statement
newPanelBlock.setStyle("skinClass",com.foldername.panelSkin);
newPanelBlock.setStyle("backgroundColor", 0x184c81);
I have an ItemRenderer with a data that has some flags. I need to change the ItemRenderer color depending on these flags. How can I achieve that? This is my IR:
<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="false"
x="{data.x}" y="{data.y}">
<s:states>
<s:State name="node"/>
<s:State name="nodeIn"/>
<s:State name="nodeOut"/>
<s:State name="nodeTrafficLight"/>
<s:State name="nodeIntersection"/>
<s:State name="nodeBlocked"/>
</s:states>
<s:Ellipse width="16" height="16" x="-8" y="-8">
<s:stroke>
<s:SolidColorStroke weight="2"
color="#FFFF00"
color.nodeTrafficLight="#FF00FF"
color.nodeIntersection="#FF9900"
color.nodeBlocked="#000000"
color.nodeIn="#00FF00"
color.nodeOut="#FF0000"/>
</s:stroke>
<s:fill>
<s:SolidColor alpha=".5"
color="#FFFF00"
color.nodeTrafficLight="#FF00FF"
color.nodeIntersection="#FF9900"
color.nodeBlocked="#000000"
color.nodeIn="#00FF00"
color.nodeOut="#FF0000"/>
</s:fill>
</s:Ellipse>
<fx:Script>
<![CDATA[
override protected function getCurrentRendererState():String
{
if (data.isBlocked)
return "nodeBlocked";
if (data.isIn)
return "nodeIn";
if (data.isOut)
return "nodeOut";
if (data.isIntersection)
return "nodeIntersection";
return "node";
}
]]>
</fx:Script>
</s:ItemRenderer>
But I don't get these states updated when some of these flags (eg. data.isIn) change. My model has [Bindable] tag.
First off; I don't think the x and y values have an affect inside a renderer. The list class will handle the positioning of the renderer.
That said, you're renderer does not respond to the dataChange event; so the renderer will not update itself when the data changes. Add a dataChange event handler:
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="false"
dataChange="onDataChange(event)">
<fx:Script>
<![CDATA[
protected function onDataChange(event:Event):void{
invalidateRendererState();
}
]]>
</fx:Script>
The invalidateRendererState() method should force commitProperties() to re-run; which will in turn force getCurrentRendererState() to execute.
If for some reason the onDataChange() event is not firing when you change the data in your dataProvider; you'll have to use the itemUpdated() or refresh() function on your dataProvider to 'announce' that you changed data.
hostComponent seems to have stopped working as it used to before. If I create a custom comp based on lets say SkinnableContainer and apply default skin I am unable to see co hinting for Bindable/public variables from the skin. Code below to illustrate.
What am I missing here? Same seems to happen with other components/skins. I'm using latest Flash Builder (4.6).
<--------- Component --------------->
<?xml version="1.0" encoding="utf-8"?>
<s:SkinnableContainer 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" skinClass="skins.testSkin">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
[Bindable]
public var test:String = "Test";
]]>
</fx:Script>
</s:SkinnableContainer>
<----------------- Skin -------------->
<?xml version="1.0" encoding="utf-8"?>
<s:Skin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009" alpha.disabled="0.5">
<fx:Metadata>
<![CDATA[
/**
* #copy spark.skins.spark.ApplicationSkin#hostComponent
*/
[HostComponent("spark.components.SkinnableContainer")]
]]>
</fx:Metadata>
<fx:Script fb:purpose="styling">
<![CDATA[
/**
* #private
*/
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number) : void
{
this.hostComponent
// Push backgroundColor and backgroundAlpha directly.
// Handle undefined backgroundColor by hiding the background object.
if (isNaN(getStyle("backgroundColor")))
{
background.visible = false;
}
else
{
background.visible = true;
bgFill.color = getStyle("backgroundColor");
bgFill.alpha = getStyle("backgroundAlpha");
}
super.updateDisplayList(unscaledWidth, unscaledHeight);
}
]]>
</fx:Script>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<!--- Defines the appearance of the SkinnableContainer class's background. -->
<s:Rect id="background" left="0" right="0" top="0" bottom="0">
<s:fill>
<!--- #private -->
<s:SolidColor id="bgFill" color="#FFFFFF"/>
</s:fill>
</s:Rect>
<!--
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
-->
<!--- #copy spark.components.SkinnableContainer#contentGroup -->
<s:Group id="contentGroup" left="0" right="0" top="0" bottom="0" minWidth="0" minHeight="0">
<s:layout>
<s:BasicLayout/>
</s:layout>
</s:Group>
</s:Skin>
In order for Flash Builder to be able to provide you with code hinting for the public methods and properties of a custom component, the custom component needs to be specified in the HostComponent metadata directive within the skin. Currently, the skin code you provided has:
[HostComponent("spark.components.SkinnableContainer")]
Try changing that to whatever your custom component is, eg. com.mydomain.MyComponent.
I'm trying to use an image (embedded) for the thumb on my Spark slider component. This should be straightforward -- I know what to do in mx -- but I haven't been able to find what the Spark syntax is.
I don't even need states (up, down, over, etc) since this is going on a mobile app.
Does anyone know?
Thanks.
Your Spark Slider requires a custom skin class, and that skin requires a SliderThumbSkin for the thumb.
Spark Slider:
<?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">
<s:VSlider skinClass="SliderSkin" />
</s:Application>
SliderSkin - Spark Slider's skin class requires a thumb skin:
<!--- The default skin class is VSliderThumbSkin.
#copy spark.components.supportClasses.TrackBase#thumb
#see spark.skins.spark.VSliderThumbSkin -->
<s:Button id="thumb" left="0" right="0" width="11" height="11"
tabEnabled="false"
skinClass="SliderThumbSkin" />
This is the only change to the VSliderSkin. If you need the entire skin code, it is below in the Additional Information section of this answer.
SliderThumbSkin - Thumb skin's button thumb requires skin for images:
<?xml version="1.0" encoding="utf-8"?>
<s:SparkButtonSkin xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
minWidth="21"
minHeight="21"
alpha.disabled="0.5">
<fx:Metadata>
<![CDATA[
/**
* #copy spark.skins.spark.ApplicationSkin#hostComponent
*/
[HostComponent("spark.components.Button")]
]]>
</fx:Metadata>
<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<s:Image source.up="https://www.google.com/intl/en_com/images/srpr/logo3w.png"
source.over="https://www.google.com/intl/en_com/images/srpr/logo3w.png"
source.down="https://www.google.com/intl/en_com/images/srpr/logo3w.png"
source.disabled="https://www.google.com/intl/en_com/images/srpr/logo3w.png"
width="20"
height="20" />
</s:SparkButtonSkin>
Here, I've replaced the thumb with the Google logo:
Additional Information
These skins can be auto-generated using Flash Builder's content assist by [CTRL/COMMAND+SPACE] inside the quotes of skinClass="" appearing as such:
This will bring up a dialog to create a new MXML skin:
Default skins are in the SDK by theme. For Spark it'd be something like:
C:\Program Files (x86)\Adobe\Adobe Flash Builder 4.5\sdks\4.5.1\frameworks\projects\spark\src\spark\skins\spark
Here is the entire SliderSkin class
<?xml version="1.0" encoding="utf-8"?>
<!--
ADOBE SYSTEMS INCORPORATED
Copyright 2008 Adobe Systems Incorporated
All Rights Reserved.
NOTICE: Adobe permits you to use, modify, and distribute this file
in accordance with the terms of the license agreement accompanying it.
-->
<!--- The default skin class for the Spark VSlider component. The thumb and track skins are defined by the
VSliderThumbSkin and VSliderTrackSkin classes, respectively.
#see spark.components.VSlider
#see spark.skins.spark.VSliderThumbSkin
#see spark.skins.spark.VSliderTrackSkin
#langversion 3.0
#playerversion Flash 10
#playerversion AIR 1.5
#productversion Flex 4
-->
<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:fb="http://ns.adobe.com/flashbuilder/2009" minWidth="11" alpha.disabled="0.5">
<fx:Metadata>
<![CDATA[
/**
* #copy spark.skins.spark.ApplicationSkin#hostComponent
*/
[HostComponent("spark.components.VSlider")]
]]>
</fx:Metadata>
<fx:Script fb:purpose="styling">
/* Define the skin elements that should not be colorized.
For slider, the skin itself is colorized but the individual parts are not. */
static private const exclusions:Array = ["track", "thumb"];
/**
* #private
*/
override public function get colorizeExclusions():Array {return exclusions;}
/**
* #private
*/
override protected function initializationComplete():void
{
useChromeColor = true;
super.initializationComplete();
}
</fx:Script>
<fx:Script>
/**
* #private
*/
override protected function measure() : void
{
// Temporarily move the thumb to the top of the Slider so measurement
// doesn't factor in its y position. This allows resizing the
// VSlider to less than 100px in height.
var thumbPos:Number = thumb.getLayoutBoundsY();
thumb.setLayoutBoundsPosition(thumb.getLayoutBoundsX(), 0);
super.measure();
thumb.setLayoutBoundsPosition(thumb.getLayoutBoundsX(), thumbPos);
}
</fx:Script>
<s:states>
<s:State name="normal" />
<s:State name="disabled" />
</s:states>
<fx:Declarations>
<!--- The tooltip used in the mx.controls.Slider control.
To customize the DataTip's appearance, create a custom VSliderSkin class. -->
<fx:Component id="dataTip">
<s:DataRenderer minHeight="24" minWidth="40" x="20">
<s:Rect top="0" left="0" right="0" bottom="0">
<s:fill>
<s:SolidColor color="0x000000" alpha=".9"/>
</s:fill>
<s:filters>
<s:DropShadowFilter angle="90" color="0x999999" distance="3"/>
</s:filters>
</s:Rect>
<s:Label id="labelDisplay" text="{data}"
horizontalCenter="0" verticalCenter="1"
left="5" right="5" top="5" bottom="5"
textAlign="center" verticalAlign="middle"
fontWeight="normal" color="white" fontSize="11">
</s:Label>
</s:DataRenderer>
</fx:Component>
</fx:Declarations>
<!--- The default skin class is VSliderTrackSkin.
#copy spark.components.supportClasses.TrackBase#track
#see spark.skins.spark.VSliderTrackSkin -->
<s:Button id="track" left="0" right="0" top="0" bottom="0" minHeight="33" height="100"
tabEnabled="false"
skinClass="spark.skins.spark.VSliderTrackSkin" />
<!--- The default skin class is VSliderThumbSkin.
#copy spark.components.supportClasses.TrackBase#thumb
#see spark.skins.spark.VSliderThumbSkin -->
<s:Button id="thumb" left="0" right="0" width="11" height="11"
tabEnabled="false"
skinClass="SliderThumbSkin" />
</s:SparkSkin>
using flex, is it possible to make a square, drawn using s:rect , clickable?
I am trying to draw a series of coloured boxes and allow them to be clicked on to perform an action.
I wasn't able to target it directly so I wrapped it in a BorderContainer and that did the trick. Or you could just use a BorderContainer if all you want is a box you can color and target.
<?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" creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.controls.Alert;
public function init():void{
myBox.addEventListener(MouseEvent.MOUSE_OVER, changeColor);
}
public function changeColor(e:MouseEvent):void
{
myFill.color = 0xFFFF00;
}
]]>
</fx:Script>
<s:states>
</s:states>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<s:SolidColor id="myFill" color="0xFF0000" />
<s:SolidColorStroke id="myStroke" color="0x000000" weight="2" />
</fx:Declarations>
<s:BorderContainer id="myBox" >
<s:Rect width="200" height="200" fill="{myFill}" stroke="{myStroke}" id="box1" />
</s:BorderContainer>
</s:Application>