Creating Custom Effects

Although you can use standard effects and composite effects to solve most of an application’s effects requirements, sometimes these off-the-shelf solutions won’t achieve the intended result. For those cases, the Flex framework allows you to create your own custom effects that you can use exactly as you would use other standard effects.

Creating custom effects requires a more thorough understanding of the effect framework structure. Because working with effects is so simple, it’s not necessary to look at the inner workings of the effect framework until you want to write a custom effect.

The effect framework consists of two basic types of classes: effect factories and effect instances. When you create a new effect object using MXML or ActionScript, you are working with an effect factory class. However, when the effect is applied to a component, the actual object utilized is an effect instance (one that is created automatically behind the scenes). The effect objects that you create using MXML and/or ActionScript using classes such as Move or Resize utilize a design pattern called the Factory Method. The Factory Method pattern means that the factory class is responsible for creating the effect instances, which are what are applied to the components.

Next we’ll look at how to define factory and instance classes.

Defining an Effect Instance Class

The effect instance class is the one used as the blueprint for the actual objects that apply the effect to the components. You don’t directly create instances of this class normally. That is handled by the factory class. For example, when you use a move effect, the actual effect object that is applied to a component is of type MoveInstance. You don’t typically create a MoveInstance object directly. Rather, that instance is automatically created by the Move factory object. We’ll look at how to create factories in the next section. First, let’s look at how to create an effect instance class.

All effect instance classes must inherit from mx.effects.EffectInstance, and at a minimum, all EffectInstance subclasses must override the play() method, and the overridden play() method must also call the super.play() method. Additionally, all effect instance classes should have constructors that accept one parameter typed as Object. The parameter is the target for the effect instance that is automatically passed to the constructor when it is called by the factory. The constructor should call super() and pass it the parameter. Example 13-11 is a simple example that merely places a red dot in the upper-right corner of a component.

Example 13-11. FlagInstance class as an example of a custom effect instance

package com.oreilly.programmingflex.effects {

    import mx.effects.EffectInstance;
    import flash.display.Shape;
    // The class must extend EffectInstance.
    public class FlagInstance extends EffectInstance {

        // Allow for configuration of the color.
        private var _color:Number;

        public function set color(value:Number):void {
            _color = value;
        }

        public function get color():Number {
            return _color;
        }

        // The constructor must accept a parameter and pass that
        // along to the super constructor.
        public function FlagInstance(newTarget:Object) {
            super(newTarget);
        }

        // All effect instances must override play().
        override public function play():void {

            // Call the super.play() method.
            super.play();

            // Create a shape with a red dot.
            var shape:Shape = new Shape();
            shape.graphics.lineStyle(0, 0, 0);
            shape.graphics.beginFill(_color, 1);
            shape.graphics.drawCircle(0, 0, 5);
            shape.graphics.endFill();

            // Move the shape to the upper-right corner of the component.
            shape.x = target.x + target.width;
            shape.y = target.y;

            // Add the shape to the display list.
            target.parent.rawChildren.addChild(shape);
        }

    }
}

Defining an Effect Factory Class

All effect factory classes must extend the mx.effects.Effect class. When you subclass Effect, you must override the getAffectedProperties() and initInstance() methods, and you must assign a reference to the instanceClass property.

The getAffectedProperties() method should return an array of all the names of the properties affected. If the effect doesn’t affect any properties, the method should return an empty array.

The initInstance() method should accept one parameter of type IEffectInstance. It should always call super.initInstance(), and then it should set any necessary properties of the instance. For example, if you want to pass through any settings from the factory to the instance, you should do so in the initInstance() method.

The instanceClass property is a property inherited from Effect that determines what class is used by the factory to create instances. You must set the instanceClass property. Typically, you should do this in the constructor.

Example 13-12 is a simple factory class corresponding to the FlagInstance class from the preceding section.

Example 13-12. Flag class as an example of an effect

package com.oreilly.programmingflex.effects {
    import mx.effects.Effect;
    import mx.effects.IEffectInstance;

    // All factory classes must inherit from Effect.
    public class Flag extends Effect {

        // Allow for the configuration of the color. Use a default of red.
        private var _color:Number = 0xFF0000;

        public function set color(value:Number):void {
            _color = value;
        }

        public function get color():Number {
            return _color;
        }

        // The constructor must call the super constructor, and it should also
        // assign the instance class reference to instanceClass.
        public function Flag(newTarget:Object = null) {
            super(newTarget);
            instanceClass = FlagInstance;
        }

        // In this example there are no affected properties for the target.
        override public function getAffectedProperties():Array {
            return [];
        }

        override protected function initInstance(instance:IEffectInstance):void {
            super.initInstance(instance);

            // Since instance is typed as EffectInstance you must cast as FlagInstance
            // to set the color property.
            FlagInstance(instance).color = _color;
        }

    }
}

Using Custom Effects

Once you’ve created a custom effect, you can use it just as you would use a standard effect. The following example illustrates this by applying the flag effect to text input controls as the user moves focus:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
xmlns:pf="com.oreilly.programmingflex.effects.*">

    <mx:VBox x="164" y="187">
        <mx:TextInput focusOutEffect="{flagEffect}" />
        <mx:TextInput focusOutEffect="{flagEffect}" />
        <mx:TextInput focusOutEffect="{flagEffect}" />
        <mx:TextInput focusOutEffect="{flagEffect}" />
    </mx:VBox>
    <pf:Flag id="flagEffect" color="0xFFFFFF" />

</mx:Application>

Creating Tween Effects

Thus far, you’ve seen how to create a custom effect that essentially has two states: off and on. For example, the flag effect that you saw in the previous sections is either not applied or applied, but there’s no transition between these two states. Yet sometimes an effect should take place over a period of time. For example, the standard Flex move effect takes place over time by changing the x and y properties of the target incrementally, until the target has reached the destination over the allotted duration. When you want to create effects that cause changes over time, you should create a tween effect. Tween effect classes extend TweenEffect and TweenEffectInstance rather than Effect and EffectInstance. We’ll talk more about that in just a moment. First, we’ll look at how to use the mx.effects.Tween class to create animated changes.

The Tween class constructor requires that you pass it four parameters: a callback object, a starting value, an ending value, and duration in milliseconds. For example, the following creates a Tween object that automatically sends notifications at intervals for 5,000 milliseconds. Each notification includes a value from 0 to 100. The progression of values is a linear change from 0 to 100. The notifications are sent to the this object.

new Tween(this, 0, 100, 5000);

Unlike most of the Flex framework, the Tween class does not use the normal event model. Instead, it uses a callback model in which the callback object must define methods with specific names. Those methods are then called in response to specific events. In the case of the Tween class, the callback object can define the onTweenUpdate() and onTweenEnd() methods. The onTweenUpdate() method receives notifications as the value changes over time. The onTweenEnd() method receives notifications when the tween has completed.

Once you construct a Tween object, it automatically starts to run. It calls the methods on the callback method at the appropriate intervals, sending the current value of the range over which it is changing over time. For example, on the first onTweenUpdate() method call, the Tween object passes it a value of 0 based on the preceding example, but the second call to onTweenUpdate() will be a value slightly larger than 0, with each successive call passing the method a larger value. Once the value reaches the maximum value in the range (100 in our example), the Tween object calls onTweenEnd().

Most of the standard effects, such as move, rotate, and blur, are tween effects. Because TweenEffect and TweenEffectInstance extend Effect and EffectInstance, respectively, implementing a concrete subclass of each of these types is very similar to implementing classes that directly extend Effect and EffectInstance. In fact, all the rules discussed in the preceding sections are applicable to tween effects as well. Apart from extending TweenEffect, all tween effect factory classes have the same rules as nontween effects. Tween effect instance classes, however, must follow several rules.

TweenEffectInstance subclasses should construct a Tween object in the play() method, and the Tween object should use the this object as the callback object. Furthermore, the subclass must override the onTweenUpdate() method at a minimum. The onTweenUpdate() method should accept one parameter typed as Object. And the onTweenUpdate() method should be responsible for updating the property or properties of the target.

In Example 13-13, WobbleInstance is a TweenEffectInstance subclass that uses Tween objects to cause the target to appear to wobble a specified number of times.

Example 13-13. WobbleInstance class as an example of a tween effect instance

package com.oreilly.programmingflex.effects {

    import mx.effects.effectClasses.TweenEffectInstance;
    import mx.effects.Tween;

    // The class must extend TweenEffectInstance.
    public class WobbleInstance extends TweenEffectInstance {

        // The _wobbleRepeat property determines how many times the target should
        // wobble. The _wobbleCount property counts how many wobbles have occurred.
        private var _wobbleRepeat:uint;
        private var _wobbleCount:uint;

        public function set wobbleRepeat(value:uint):void {
            _wobbleRepeat = value;
        }

        public function get wobbleRepeat():uint {
            return _wobbleRepeat;
        }

        // The constructor looks very much like a regular Effect subclass.
        public function WobbleInstance(newTarget:Object) {
            super(newTarget);
        }

        // The play() method calls super.play(). Then it creates a new Tween object.
        // In this case the Tween object changes from 0 to 2 over the course of 100
        // milliseconds.
        override public function play():void {
            super.play();
            _wobbleCount = 0;
            new Tween(this, 0, 2, 100);
        }

        // The onTweenUpdate() method is required. In this case onTweenUpdate() simply
        // sets the rotation property of the target.
        override public function onTweenUpdate(value:Object):void {
            super.onTweenUpdate(value);
            target.rotation = value;
        }

        // The onTweenEnd() method is not strictly required. However, in this case we
        // need to override it so that it can create new Tween objects for as long as
        // the target is supposed to wobble.
        override public function onTweenEnd(value:Object):void {
            super.onTweenEnd(value);
            if(_wobbleCount < _wobbleRepeat) {
                new Tween(this, value, value == 2 ? −2 : 2, 200);
            }
            else if(_wobbleCount == _wobbleRepeat) {
                new Tween(this, value, 0, 100);
            }
            _wobbleCount++;
        }

    }
}

Example 13-14 shows the Wobble factory class. Notice that it looks very similar to a regular effect factory class.

Example 13-14. Wobble class as an example tween effect factory

package com.oreilly.programmingflex.effects {
    import mx.effects.TweenEffect;
    import mx.effects.IEffectInstance;

    public class Wobble extends TweenEffect {

        private var _wobbleRepeat:uint = 2;

        public function set wobbleRepeat(value:uint):void {
            _wobbleRepeat = value;
        }

        public function get wobbleRepeat():uint {
            return _wobbleRepeat;
        }

        public function Wobble(newTarget:Object = null) {
            super(newTarget);
            instanceClass = WobbleInstance;
        }

        override public function getAffectedProperties():Array {
            return ["rotation"];
        }

        override protected function initInstance(instance:IEffectInstance):void {
            super.initInstance(instance);
            WobbleInstance(instance).wobbleRepeat = _wobbleRepeat;
        }

    }
}

Example 13-15 shows the effect applied to components.

Example 13-15. Applying a custom tween effect

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
xmlns:pf="com.oreilly.programmingflex.effects.*">

    <mx:VBox x="164" y="187">
        <mx:TextInput focusOutEffect="{wobbleEffect}" />
        <mx:TextInput focusOutEffect="{wobbleEffect}" />
        <mx:TextInput focusOutEffect="{wobbleEffect}" />
        <mx:TextInput focusOutEffect="{wobbleEffect}" />
    </mx:VBox>

    <pf:Wobble id="wobbleEffect" wobbleRepeat="10" />

</mx:Application>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset