Custom Component in Flex 4 (Gumbo). A Knob Button - part 1.

February 26, 2009. Posted by Andy Hulstkamp under Actionscript 3 FXG Gumbo (Flex 4 beta)
Keywords: , , , , ,

In this and this post I had a look at skinning components in Flex 4. Here I had a go at creating a simple custom component in Flex 4 (Gumbo) from scratch. The goal was to create a simple knob-button like those found on many softsynths.

The user clicks the button and drags the mouse to adjust the value. The advantage of a knob compared to a slider is that it needs much less screen estate and could be more intuitive in specific contexts to control a value.

Custom Knob Component

click here to view the demo. Full source at the end of this post.

Creating a custom visual component in Flex 4 is similar to the procedure in Flex 3 but states and skins are handled differently (in a much better and cleaner way). Basically we need to do (some of) the following stuff:

  • Decide which component we want to inherit from
  • Define SkinParts
  • Define SkinState
  • Keep track of the state and leverage this into an overriden getCurrentSkinState() function
  • override partAdded() and partRemoved() to (un)register handlers to the skin parts and do setup/housecleaning code for the skinparts
  • Implement the behaviour (EventHandlers or whatever)
  • Keep track of the internal data and manage properties
  • Override Flex Component Lifecycle (as in HALO) methods if needed
  • Push relevant data down to the skin (or pull from the hostComponent inside the skin)
  • Define the skin class and the required SkinParts

The Spark UI Components in Flex 4 follow a lightweight approach (which in my opinion is the much better approach since there’s less overhead and usually better performance). So for the knob button I just wanted the following features (similar to the Slider-Component):

  • minValue
  • maxValue
  • minRotation
  • maxRotation

No additional features, like showing the value in a text field should be defined. Extended features can later be implemented by using composition or extending from this component. See part 2 for this.

Here are some excerpts that define the Spark specific parts, otherwise the procedure is similar to creating a component in flex 3. You can get the full source code here.

    //--------------------------------------------------------------------------
    //
    //  States & Skin Parts
    //
    //--------------------------------------------------------------------------
 
     /**
	 *  Up State of the Button
	 */
	[SkinState("up")]
 
	/**
	 *  Over State of the Button
	 */
	[SkinState("over")]
 
	/**
	 *  Down State of the Button
	 */
	[SkinState("down")]
 
	/**
	 *  Disabled State of the Button
	 */
	[SkinState("disabled")]
 
    protected var isOver:Boolean;
    protected var isDown:Boolean;
 
     /**
     *  A skin part that defines the knob.
     *  TODO: Use a strict type once the Skin is defined
     */
    [SkinPart(required="true")]
    public var knob:*;
 
     /**
     *  Add Handlers to the knob part
     */
    override protected function partAdded(partName:String, instance:Object):void
    {
        super.partAdded(partName, instance);
        if (partName == "knob") {
        	  addInitialHandlers();
        }
    }
 
    /**
     * Remove Handlers from the knob part
     * @param partName
     * @param instance
     *
     */
    override protected function partRemoved(partName:String, instance:Object):void {
    	 super.partRemoved(partName, instance);
    	 if (partName == "knob") {
        	  removeInitialHandlers();
        	  removeMoveHandlers();
        }
    }
 
    /**
     * This is called by the Framework. We need to keep track of the state and
     * return it here. Called by Flex after a call to invalidateSkinState()
     * @return The current state of the Component
     *
     */
    override protected function getCurrentSkinState():String
    {
         if (!enabled)
            return "disabled";
 
        if (isDown)
            return "down";
 
        if (isOver)
            return "over";
 
        return "up";
    }

The rest is just implementing the controller code (handlers) and keep track of the data and managing properties.

The skin for the simple knob has to define the requested SkinPart for the knob. It’s something similar to this:

<!-- states -->
	<states>
		<State name="up" />
		<State name="over" />
		<State name="down" />
		<State name="disabled" />
	</states>
 
	<!-- The required Knob Skin Part -->
	<Ellipse id="knob" width="{this.width}" height="{this.height}" horizontalCenter="0" verticalCenter="0">
		 <stroke>
            <SolidColorStroke  weight="2" weight.over="3" weight.down="4" color="{c2}" color.over="{c2}" color.down="{c1}">
 
            </SolidColorStroke>
        </stroke>
        <fill>
        	<LinearGradient >
                    <entries>
                        	<GradientEntry color="{c1}" ratio="0"  />
                         	<GradientEntry color="{c2}" ratio=".35"  />
                         	<GradientEntry color="{c3}" ratio=".4"  />
                          	<GradientEntry color="{c4}" ratio="1"  />
                    </entries>
                </LinearGradient>
        </fill>
	</Ellipse>
 
	<!-- Additional stuff to spice up the knob. Not Required -->
	<Ellipse blendMode="overlay" width="{this.width}" height="{this.height}" horizontalCenter="0" verticalCenter="0">
        <fill>
        	<RadialGradient focalPointRatio="-.5" rotation="45">
        		<entries>
        			<GradientEntry color="0xffffff" ratio="0"   />
                     <GradientEntry color="0xd0d0d0" ratio=".65"  />
                     <GradientEntry color="0xb0b0b0" ratio=".85"  />
                     <GradientEntry color="0xa0a0a0" ratio="1"  />
        		</entries>
        	</RadialGradient>
 
        </fill>
	</Ellipse>
 
	<!-- A filter to make it a little more interesting. Not required -->
	<filters>
       <DropShadowFilter blurX="4" blurY="4" alpha="0.32" distance="3" distance.down="0" angle="45" knockout="false" />
    </filters>

Full source code can be found here. (Flex SDK 4.0.0.4932)



Share/Save/Bookmark

9 Responses to “Custom Component in Flex 4 (Gumbo). A Knob Button - part 1.”

  1. Joe Says:

    These knobs are pretty cool, but a knob that points at the mouse position is terrible usability for a number of reasons, among them the difficulty in precisely representing a large range of values and the jarring visual of the knob suddenly jumping from 270 to 90 as the mouse crosses the center point. Yet, almost everyone builds this poor usability into their knobs. A knob should activate when on mouse click and should track to mouse movement, not to mouse position–vertical up movement moves the knob clockwise and vertical down movement moves it counter-clockwise.

  2. Andy Hulstkamp Says:

    Joe

    The focus of this post was to get familiar with custom components in gumbo - I didn’t give too much attention to the handling of the knob, but thinking of it, you’re absolutely right. Using vertical movements to adjust the value would give a much better user-experience. It’s straight forward to overwrite the mouseHandlers to get this interaction and I’ll keep this in mind the next time I’ll revisit knobs.

    thanks

  3. Gertjan Smit Says:

    I am busy with the making of controls with Flash artwork used in Flex. They are pretty good tools to make buttons and stuff.
    When i see these knobs i am pretty impressed, but as all good things there is allways room to improve it.
    Like the suggestion of Joe, well maybe i use them in my current project with some minor adjustments. Don’t know which yet.

  4. Mr Smith Says:

    I am looking at the image of ‘a big one’ (knob button) and am curious as to how well it handles rollover events. I find on repeated rollover events that bigger knob buttons actually scale larger over time to the point that it discharges one huge dispatch? Do yours do the same? I hope not as I am not a particular fan of growing knobs on frequent user interaction!

  5. Jeff Says:

    Hi,

    Got to say these knobs are really impressive! I’ve been playing with them all day.

  6. Andy Hulstkamp Says:

    @Smtih

    Don’t think so, haven’t tested them thoroughly, though. As for controls that scale larger over time involving scaling on rollovers-/outs, guess it’s either a rounding problem or the upscale and downscale is not done properly - or the framework takes elements like border and the like inconsistently into account. One way to prevent this, is to explicitly set the initial width and height at rollout.

  7. prasad Says:

    Thanks for the information.

    Regards

  8. Pollen Sepal Says:

    The title says flex 4, but it’s flex 3 SDK?

  9. Andy Hulstkamp Says:

    Pollen,

    It’s Flex 4, the SDK used is Flex 4 beta (Gumbo).

Leave a Reply