Flex 4 Changing style options with states - apache-flex

I'm interested in finding out the best approach to this issue, it's not technically difficult but there must be an elegant solution.
Basically i have a form that features mostly text inputs, i would like to change the style of the input boxes based on the current state.
I can do this in the mxml on each input...
<s:TextInput text="label" borderColor.State1="0xFFFFFF" borderColor.State2="0x000000"/>
But that involves creating properties on every single item in the form.
There must be a better way of doing this without adding a property to each item?
Thanks!

You can use transitions and SetAction to set styles on multiple objects at the same time based on a new viewstate. This is a piece of an example from the SDK documentation:
.
<s:states>
<s:State name="Login" />
<s:State name="Register" />
</s:states>
<s:transitions>
<!-- Define the transition from the base state to the Register state.-->
<s:Transition id="toRegister" fromState="*" toState="Register">
<s:Sequence targets="{[loginPanel, registerLink, confirm, loginLink]}">
<s:RemoveAction />
<s:Fade />
<s:SetAction target="{loginPanel}" property="title" />
<s:SetAction target="{loginButton}" property="label" />
<s:SetAction target="{loginButton}" property="color" />
<s:Resize target="{loginPanel}"/>
<s:AddAction />
<s:Fade />
</s:Sequence>
</s:Transition>
<!-- Define the transition from the Register state to the base state.-->
<s:Transition id="toDefault" fromState="Register" toState="*">
<s:Sequence targets="{[loginPanel, registerLink,
confirm, loginLink]}">
<s:RemoveAction/>
<s:SetAction target="{loginPanel}" property="title"/>
<s:SetAction target="{loginButton}" property="label"/>
<s:SetAction target="{loginButton}" property="color"/>
<s:Resize target="{loginPanel}"/>
<s:AddAction/>
</s:Sequence>
</s:Transition>
</s:transitions>
You can just target everything in the sequence (instead using different targets for each SetAction) and use the 'value' property of the SetAction to set the values to what you want.

Related

Flex 4 Easy Way To Transition Between States

Is there an easy way, using only MXML, to transition between states that add and remove elements.
For example:
<s:Group includeIn="state1"/>
<s:Group includeIn="state2"/>
Can MXML transitions slide state1 out and state2 in, for example?
Thank you.
In addition to the link posted above here's a way to apply a single transition to multiple targets:
<?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:views="views.*" >
<s:states>
<s:State name="home"/>
<s:State name="help"/>
<s:State name="instructions"/>
<s:State name="settings"/>
<s:State name="feedback"/>
</s:states>
<s:transitions>
<s:Transition fromState="*" toState="*" >
<s:Sequence >
<s:Wipe direction="right" duration="350" target="{this}"/>
</s:Sequence>
</s:Transition>
</s:transitions>
<views:Home id="home" includeIn="home" width="100%" height="100%" ownerComponent="{this}"/>
<views:Help id="help" includeIn="help" width="100%" height="100%" ownerComponent="{this}" />
<views:Instructions id="instructions" includeIn="instructions" width="100%" height="100%" ownerComponent="{this}" />
<views:Settings id="settings" includeIn="settings" width="100%" height="100%" ownerComponent="{this}" />
<views:Feedback id="feedback" includeIn="feedback" width="100%" height="100%" ownerComponent="{this}" />
</s:Group>
The previous transition creates a wipe from one direction to the other.
The next transition fades from one view to another:
<s:transitions>
<s:Transition fromState="*" toState="home" >
<s:Sequence >
<s:Fade target="{this}" alphaFrom="1" alphaTo="0"/>
<s:RemoveAction targets="{[home,help,instructions,settings,feedback]}"/>
<s:AddAction target="{home}"/>
<s:Fade target="{this}" alphaFrom="0" alphaTo="1"/>
</s:Sequence>
</s:Transition>
</s:transitions>
The next slides one view in while sliding the rest out:
<s:transitions>
<s:Transition fromState="*" toState="home" >
<s:Sequence duration="1000" >
<s:Parallel>
<s:Move applyChangesPostLayout="true" targets="{notHomeTargets}" xFrom="0" xTo="{-width}"/>
<s:AddAction target="{home}" />
<s:Move applyChangesPostLayout="true" target="{home}" xFrom="{width}" xTo="0"/>
</s:Parallel>
<s:RemoveAction targets="{targets}" />
</s:Sequence>
</s:Transition>
</s:transitions>
You would have to create one for each state with the last examples.

How to have a glow filter in Spark

In Flex 3 you could do a glow effect like so:
<mx:Glow id="glowImage" duration="250"
alphaFrom="0" alphaTo="1"
blurXFrom="0.0" blurXTo="3.0"
blurYFrom="0.0" blurYTo="3.0" strength="2"
color="0xcc0000" target="{this}"/>
<mx:Glow id="unglowImage" duration="250"
alphaFrom="1" alphaTo="0"
blurXFrom="3.0" blurXTo="0.0"
blurYFrom="3.0" blurYTo="0.0" strength="2"
color="0xaaaaaa" target="{this}"/>
And the MXML:
<mx:Image rollOverEffect="{glowImage}" rollOutEffect="{unglowImage}"/>
In Flex 4 we are supposed to use the AnimateFilter with a GlowFilter (recommended by Adobe). Here's what I've come up with:
<s:AnimateFilter id="glowAnimation"
target="{this}"
duration="250"
>
<s:bitmapFilter>
<s:GlowFilter color="#ff0000" strength="3" quality="3" />
</s:bitmapFilter>
<s:motionPaths>
<s:SimpleMotionPath valueFrom="0" valueTo="4" property="blurX"/>
<s:SimpleMotionPath valueFrom="0" valueTo="4" property="blurY"/>
</s:motionPaths>
</s:AnimateFilter>
And the MXML:
<mx:Image rollOverEffect="{glowAnimation}" rollOutEffect="{unglowImage}"/>
The problem is that this animates once and then the effect is removed where as the mx effect applies the filter and keeps it applied.
~~~ UPDATE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RIAStar's answer is right and I want to add the reason why from this video I just stumbled upon [1]. It describes the differences between Animate and AnimateFilter. AnimateFilter is transient. It applies the filter and when it's done it removes it. Animate applies the values to the target permanently.
We also should be aware that in Flex 4 not all effects support triggers.
From the AnimateInstance class:
override public function startEffect():void
{
// This override removes EffectManager.effectStarted() to avoid use of
// mx_internal. New effects are not currently triggerable, so
// this should not matter
}
What this means is that we cannot rely on rollOverEffect to trigger the effect bound to it. We must call effect.play() on the rollOver event.
So that means when we use the Animate class we also need to change this:
<s:Image rollOverEffect="{glowImage}" rollOutEffect="{unglowImage}"/>
to this:
<s:Image rollOver="glowImage.play()" rollOut="unglowImage.play()"/>
Note when using multiple related effects it is best practice to call end() on a playing effect before calling play() on it's related effect.
Also, note when using a single effect, as in the answer, reverse it rather than end it like so glowAnimation.play(null, true).
<s:Image rollOver="rollOverAnimation.play()" rollOut="rollOverAnimation.play(null, true)"/>
I don't know about AnimateFilter, but this solution using a simple Animate effect should work nicely:
<s:Animate id="glowAnimation" target="{glow}" duration="250">
<s:motionPaths>
<s:SimpleMotionPath valueFrom="0" valueTo="10" property="blurX"/>
<s:SimpleMotionPath valueFrom="0" valueTo="10" property="blurY"/>
</s:motionPaths>
</s:Animate>
<s:Image rollOver="glowAnimation.play()"
rollOut="glowAnimation.play(null, true)">
<s:filters>
<s:GlowFilter id="glow" blurX="0" blurY="0" />
</s:filters>
</s:Image>
Note that I set the initial values of blurX and blurY to 0. This is necessary because otherwise it will display the default 4 as long as you don't roll over the image.
I use transitions for effect switching within SparkSkin. For bluring a skinned item under cursor:
<!-- states -->
<s:states>
<s:State name="up" />
<s:State name="over" />
<s:State name="down" />
<s:State name="disabled" />
</s:states>
<fx:Declarations>
<s:BlurFilter id="aBlurFilter" blurX="0" blurY="0" blurX.over="10" blurY.over="10" quality="{BitmapFilterQuality.HIGH}" />
</fx:Declarations>
<s:transitions>
<s:Transition fromState="*" toState="over">
<s:Animate id="BlurFilterTo"
target="{aBlurFilter}"
repeatCount="1"
duration="500"
repeatBehavior="reverse"
>
<s:SimpleMotionPath property="blurX" valueFrom="0" valueTo="10"/>
<s:SimpleMotionPath property="blurY" valueFrom="0" valueTo="10"/>
</s:Animate>
</s:Transition>
<s:Transition fromState="over" toState="*">
<s:Animate id="BlurFilterFrom"
target="{aBlurFilter}"
repeatCount="1"
duration="500"
repeatBehavior="reverse"
>
<s:SimpleMotionPath property="blurX" valueFrom="10" valueTo="0"/>
<s:SimpleMotionPath property="blurY" valueFrom="10" valueTo="0"/>
</s:Animate>
</s:Transition>
</s:transitions>
<s:BitmapImage id="iconDisplay" filters="{[aBlurFilter]}" />

cannot resolve the fromState attribute

What is the problems in this transition?? The error is Flex cannot resolve the fromState attribute.
<s:transitions>
<s:Transition fromState="Viewer" toState="Editor">
<s:Fade target="{viewerProductDetail}" duration="1000"/>
</s:Transition>
<s:Transition formState="Editor" toState="Viewer">
<s:Fade target="{editorProductDetail}" duration="1000"/>
</s:Transition>
</s:transitions>
The second transition specifies "formState" not "fromState". Are you sure you're reading the error correctly?

Flex 4 state transition Move effect in a VGroup

I am trying to create a nice state transition where I have 2 containers (in the example below I have used panels).
When the state changes, I want to fade away the upper panel, then move the lower panel to the top of the screen, and below that I want to fade in a new 'lower' panel.
In the code below, the fades are working fine, but the panel doesn't move to the top of the box, it just goes to it's new position without a transition.
Also the 'reverse' transition doesn't happen at all. I tried to set autoReverse to true, and I also tried to build an opposite transition, both no result, there was no transition.
When I replace the VGroup (in which it all happens) for VBox, I get a slightly better result, the transition works in one way. The reverse transition still doesn't work at all.
<?xml version="1.0" encoding="utf-8"?>
<fx:Script>
<![CDATA[
private function switchMode():void
{
if (currentState == "up")
currentState = "down";
else
currentState = "up";
}
]]>
</fx:Script>
<s:states>
<s:State name="up" />
<s:State name="down" />
</s:states>
<s:transitions>
<s:Transition fromState="up" toState="down">
<s:Sequence>
<s:Fade target="{upperGrid}" />
<s:RemoveAction target="{upperGrid}" />
<s:Move target="{panel1}" />
<s:AddAction target="{lowerGrid}" />
<s:Fade target="{lowerGrid}" />
</s:Sequence>
</s:Transition>
<s:Transition fromState="down" toState="up">
<s:Sequence>
<s:Fade target="{lowerGrid}" />
<s:RemoveAction target="{lowerGrid}" />
<s:Move target="{panel1}" />
<s:AddAction target="{upperGrid}" />
<s:Fade target="{upperGrid}" />
</s:Sequence>
</s:Transition>
</s:transitions>
<s:VGroup width="100%" height="100%" top="10" left="10" right="10" bottom="10">
<s:Panel id="upperGrid" width="100%" height="100%" includeIn="up" title="upper panel" />
<s:Panel id="panel1" width="100%" title="Panel">
<s:Button label="Switch mode" click="switchMode()" />
</s:Panel>
<s:Panel id="lowerGrid" width="100%" height="100%" includeIn="down" title="lower panel" />
</s:VGroup>
When I get rid of the VGroup or VBox, and use absolute positions, the transitions work fine:
<s:Panel id="upperGrid" left="10" right="10" top="10" bottom="{panel1.height + 20}" includeIn="up" title="upper panel" />
<s:Panel id="panel1" left="10" right="10" top.up="{upperGrid.height + 20}" top.down="10" title="Panel">
<s:Button label="Switch mode" click="switchMode()" />
</s:Panel>
<s:Panel id="lowerGrid" left="10" right="10" top="{panel1.height + 20}" bottom="10" includeIn="down" title="lower panel" />
Should you always use absolute positioning if you want these kind of moving transitions or is it possible to use these transitions in a VGroup or VBox combined with includeIn and excludeFrom properties? And if so, how could I correct that in the example above?
The problem is that you're using a container that is meant to 'layout' it's children. You could try to create your own layout that could know when it's children are currently in an effect and not touch them until they're done, or use an absolutely positioned layout container (like Group) instead.

Can I set an effect's duration as a percentage of its parent's?

When I'm building an composite animation, I'd like to specify the components as fractions of the parent, like so:
<s:Sequence id="example" duration="2000">
<s:Fade alphaFrom="0" alphaTo="1" duration="10%"/> <!-- not legal -->
<s:Scale scaleXTo="2" scaleYTo="2"/>
<s:Fade alphaFrom="1" alphaTo="0" duration="10%"/>
</s:Sequence>
Failing that, I use an expression like so:
<s:Sequence id="example" duration="{slideTime}">
<s:Fade alphaFrom="0" alphaTo="1" duration="{slideTime * .1}"/>
<s:Scale scaleXTo="2" scaleYTo="2" duration="{slideTime * .9}"/>
<s:Fade alphaFrom="1" alphaTo="0" duration="{slideTime * .1}"/>
</s:Sequence>
Is there a more declarative way to accomplish this? In the latter case, for instance, can I at least replace the variable slideTime with a direct reference to the parent's duration?
Thanks.
In the latter case, for instance, can
I at least replace the variable
slideTime with a direct reference to
the parent's duration?
Does this work / solve it?:
<s:Sequence id="example" duration="500">
<s:Fade alphaFrom="0" alphaTo="1" duration="{example.duration * .1}"/>
<s:Scale scaleXTo="2" scaleYTo="2" duration="{example.duration * .9}"/>
<s:Fade alphaFrom="1" alphaTo="0" duration="{example.duration * .1}"/>
</s:Sequence>

Resources