Stateful skins in Flex 3, color transitions in buttons now possible
There are a some new features available for skinning in Flex 3 beta 2. Some of them are covered by Juan and NJ. But I want to focus in one specific topic: Stateful Skins (without Flash).
These are very nice additions to the framework because they keep the code clean while giving more power when it comes to changing the behavior and look and feel of the component. With that ability, you could add transitions between one state to the other or change any property between states, without leaving Flex Builder.
As an example, to create this button, I would have this code in my application:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" backgroundColor="0x3d3d3d" layout="absolute" width="400" height="200">
<mx:Style source="assets/styles/Main.css"/>
<mx:Button label="Sexy Flex Button" x="50" y="50" />
</mx:Application>
As you can see, I have a normal button and a link to a style sheet.
The style sheet contains the following code:
/* CSS file */
Button{
skin: ClassReference("skins.ButtonSkin");
fontSize:16;
fontWeight:normal;
paddingLeft:23;
paddingRight:23;
paddingTop:10;
paddingBottom:10;
color:#ffffff;
textRollOverColor:#ffffff;
cornerRadius:5;
borderThickness:1;
borderColor:#cccccc;
borderStyle:solid;
backgroundColor:#1e83b8;
}
So far, everything looks clean at this point: a button, and a style that references a skin ("skins.ButtonSkin"). A developer can work on the logic of the application without worrying about how the button looks. Later, a designer or a "designloper" can add transitions, colors and change the look and feel without breaking or modifying the application. A reference in the CSS to the skin property gives the designer the power to add a new class (ex: skins.ButtonSkin) where transitions, dropshadows and other effects are defined.
Usually (in Flex 2), we would create this skin as a programmatic skin and have some kind of logic to determine what to show depending whether this skin should be "up", "down", "over", etc, (if we used the same skin for the "upSkin", "downSkin", "overSkin", etc). Another easy way was to use an embedded asset as the skin. With these two approaches we would get one instance of the skin for each corresponding button state, making it difficult to have transitions between these skins.
With Flex 3, we can create only one class with states for each corresponding button state. I've implemented it as a MXML component, using a Canvas as the super class, because I'm lazy ;) but I could have used any UICoponent or any class that implements IStateClient.
In this canvas, I implemented a state for each skin in the button: down, over, up (I didn't add disabled because I don't use it) and I can create transitions between states. In this example, I made a color transition from blue to orange (thanks Darron for the library) and set other properties like adding filters.
Disclaimer, I'm changing the background color by calling setStyle. That is not a good practice because setStyle is very expensive. It is better to use other method to make the same effect.
The code of the ButtonSkin is the following:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:ds="com.darronschall.effects.*"
creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.controls.Button;
import mx.core.UITextField;
use namespace mx_internal;
[Bindable]
private var textField:UITextField;
private function init():void
{
var button:Button = (parent as Button);
textField = button.getTextField();
filters = [bevel, dropshadow];
}
]]>
</mx:Script>
<!-- States .............................................. -->
<mx:states>
<mx:State name="up">
<mx:SetProperty target="{textField}" name="filters" value="{[textShadow]}"/>
</mx:State>
<mx:State name="down">
<mx:SetProperty target="{this}" name="filters" value="{[bevelDown]}"/>
<mx:SetProperty target="{glow}" name="color" value="{0xf1c990}"/>
<mx:SetProperty target="{textField}" name="filters" value="{[glow]}"/>
</mx:State>
<mx:State name="over">
<mx:SetProperty target="{textField}" name="filters" value="{[glow]}"/>
<mx:SetProperty target="{bevel}" name="highlightAlpha" value="0.8"/>
</mx:State>
</mx:states>
<!-- Filters .............................................. -->
<mx:DropShadowFilter id="dropshadow" alpha="0.5" quality="3" blurX="6" blurY="6"/>
<mx:BevelFilter id="bevel" angle="90" highlightColor="0xffffff"
shadowAlpha="0" strength="3" quality="3" distance="24" highlightAlpha="0.4"/>
<mx:BevelFilter id="bevelDown" type="inner" angle="245" shadowAlpha="0.4"
highlightAlpha="0.4" distance="8" blurX="10" blurY="10" quality="4"/>
<mx:DropShadowFilter id="textShadow" alpha="0.5" quality="2"
distance="2" blurX="2" blurY="2" />
<mx:GlowFilter id="glow" color="#b86113" alpha="0.8" />
<!-- Transitions .............................................. -->
<mx:transitions>
<mx:Transition id="toOver" fromState="*" toState="over">
<ds:AnimateColor target="{this}" property="backgroundColor"
isStyle="true" toValue="0xff9600" duration="300" />
</mx:Transition>
<mx:Transition id="toUp" fromState="*" toState="up">
<ds:AnimateColor target="{this}" property="backgroundColor"
isStyle="true" toValue="0x1e83b8" duration="300" />
</mx:Transition>
<mx:Transition id="toDown" fromState="*" toState="down">
<ds:AnimateColor target="{this}" property="backgroundColor"
isStyle="true" toValue="0xff9600" duration="300" />
</mx:Transition>
</mx:transitions>
<!-- Background Images .............................................. -->
<mx:Image source="@Embed(source='assets/images/dec_left.png')"
left="5" verticalCenter="0"/>
<mx:Image source="@Embed(source='assets/images/dec_right.png')"
right="5" verticalCenter="0"/>
</mx:Canvas>
This is just the beginning. Ely Greenfield showed what is coming in Flex 4 at MAX and they are making these things even easier. I'm very excited.
I also want to see how I can use Degrafa to implement some more complex skins.
If you want to read more, I recommend checking the docs
Tink
I means the skin just has a switch statement where the state is change immediately, but applying a tween would be as easy as handing beers out to drunks.
Ben
Juan
Narciso (nj) Jaramillo
nj
Adobe Flex team
Narciso (nj) Jaramillo
nj
Nahuel
The only thing that I couldn't do was to get rid of Darron Schall's classes because the colors are in hexadecimal and the only way that I found to have a nice transition is to use his effect. Maybe Narciso has a tip for that ;)
@Tink: You can do that in Flex 2 but is not as simple as this and it would require more work.
Josh Rodgers
Tink
Ah yeah I meant the prev version of Flex 3.0 really. Pretty pointless but I thought i'd have a crack http://www.tink.ws/blog/stateful-skins-in-flex/. Just needed to override updateDisplayList and add a switch statement.
It becomes and pain in Flex 2.0 as you can't just supply 1 skin for all states.
P
jj
Narciso (nj) Jaramillo
nj
Adobe Flex team
Narciso (nj) Jaramillo
paddy
use namespace mx_internal;
p;)
Nahuel
I need that because button.getTextField(); is under that namespace.
I'm adding bevel and dropshadow to the textfield of the button. The only way to access that textfield is using its namespace.
LP
Toni
Nice example. How can I make a image transition in the button?
I want the image to fade while the button changes image.
Olumide Otuyelu
paul
-- 1118: Implicit coercion of a value with static type mx.core:IUITextField to a possibly unrelated type mx.core:UITextField. ButtonExample/skins ButtonSkin.mxml line 19 1201723829300 46 --
Can anyone tell me how to correct this error?
Thanks!
paul
paul
-- 1118: Implicit coercion of a value with static type mx.core:IUITextField to a possibly unrelated type mx.core:UITextField. ButtonExample/skins ButtonSkin.mxml line 19 1201723829300 46 --
Can anyone tell me how to correct this error?
Thanks!
Keith
Nahuel
import mx.core.UITextField;
to
import mx.core.IUITextField;
and
[Bindable]
private var textField:UITextField;
to
[Bindable]
private var textField:IUITextField;
Jason The Saj
What is the other better method to use?
???
Second, what about loading CSS dynamically, when using ClassReference?
Glenn Williams
cheers fella
Jason The Saj
1. Say I wanted to change the label value on a state change. How would I address the button's label?
2. Any thoughts on incorporating Degrafa elements into a stateful skin?
- The Saj
Nahuel Foronda
Regarding setStyle, I know that is an expensive method, but I did that because it was easy :)
When I created this example, Degrafa was not available. I think that makes more sense to do the painting with Degrafa instead of calling set style.
About your label value question, I recommend that you don't change the value in the skin, instead change it outside. You can listen to the events that the button dispatches to do it.
Addressing your question 2, yes, incorporating Degrafa is the next step. As I mentioned, that was not available at that time.
yalasta
to
import mx.core.IUITextField;
and
[Bindable]
private var textField:UITextField;
to
[Bindable]
private var textField:IUITextField;
after I got this error:
Severity and Description Path Resource Location Creation Time Id
Could not resolve <ds:AnimateColor> to a component implementation. SimpleButton/src/Components ButtonSkin.mxml line 60 1214337487515 185
also at line 64 and 68. How to fix this problem?
thanks a lot.
Paul
Laura
Sorry about that. The code is showing now.
Jeremy
pulse00
softlogic
Dave
at
http://www.askmeflash.com/tutorial/4/skinning-a-flex-button-tutorial-using-skin-in-flex