Previous Page
Next Page

Recipe 6.5. Creating Simple Buttons

Problem

You want to create an interactive button that enables a user to click and perform an action, such as submitting a form or calculating a total.

Solution

Create an instance of the SimpleButton class and create display objects for upState, downState, overState, and hitTestState. Alternatively, create a subclass of SimpleButton that describes your desired button behavior.

Use the click event to invoke a method whenever the user presses the button.

Discussion

The display list model provides an easy way to create buttons through the SimpleButton class. The SimpleButton class allows a user to interact with the display object using their mouse, and makes it easy for you to define that interaction through various button states. The possible button states, listed here, are available as properties of the SimpleButton class:



upState

A display object for the default "up" state of the button. The "up" state is shown whenever the mouse is not over the button.



overState

A display object that determines what the button looks like when the mouse moves over the button. When the mouse leaves the button area, the button moves back to the "up" state.



downState

A display object that's shown when the button is pressed (or clicked) "down". When the button is in the "over" state, the "down" state displays when the user presses the left mouse button.



hitTestState

A display object that defines a button's bounds. When the mouse moves inside of the button's hit area, the button enters the "over" state. The hitTestState is typically set to the same display object as the upState. The hitTestState is never actually displayed on-screen; it is only used for mouse tracking purposes.

A button's state is handled by the SimpleButton class, and is based on movement of the user's mouse. You don't have control over setting the internal button state (up, down, or over). Rather, you can only control which display object should appear when the button is in a particular state. By setting the state properties to different display objects, you can provide feedback to the user as they interact with the button using their mouse.

The following example creates a new SimpleButton instance and defines button states using the four state properties defined earlier. Because each state property of the button needs to be set to a DisplayObject instance, the helper method createCircle( ) is used to create different colored circle shapes to be used for the various button states:

package {
  import flash.display.*;
  import flash.events.*;

  public class SimpleButtonDemo extends Sprite {
    public function SimpleButtonDemo(  ) {
      // Create a simple button and configure its location
      var button:SimpleButton = new SimpleButton(  );
      button.x = 20;
      button.y = 20;
      
      // Create the different states of the button, using the 
      // helper method to create different colors circles
      button.upState = createCircle( 0x00FF00, 15 );
      button.overState = createCircle( 0xFFFFFF, 16 );
      button.downState = createCircle( 0xCCCCCC, 15 );
      button.hitTestState = button.upState;
      
      // Add an event listener for the click event to be notified
      // when the user clicks the mouse on the button
      button.addEventListener( MouseEvent.CLICK, handleClick );
      
      // Finally, add the button to the display list
      addChild( button );  
    }
    
    // Helper function to create a circle shape with a given color
    // and radius
    private function createCircle( color:uint, radius:Number ):Shape {
      var circle:Shape = new Shape(  );
      circle.graphics.lineStyle( 1, 0x000000 );
      circle.graphics.beginFill( color );
      circle.graphics.drawCircle( 0, 0, radius );
      circle.graphics.endFill(  );
      return circle;
    }
    
    // Event handler invoked whenever the user presses the button
    private function handleClick( event:MouseEvent ):void {
      trace( "Mouse clicked on the button" );  
    }
  }
}

After running the preceding code block, a green circle appears in the movie. When you move your mouse over the green circle, a slightly bigger white circle appears as a rollover. When you click the white circle, it turns into a slightly smaller gray circle. This visual effect is created by the SimpleButton instance changing its state based on the actions of your mouse, switching between the display objects defined in the four state properties.

To listen for events from the button instance, the addEventListener( ) method is used as described in Recipe 1.5. The click event, specified with MouseEvent.CLICK, is handled in the preceding code by the handleClick( ) method. Anytime the user clicks the button instance, the handleClick( ) method is invoked, allowing certain actions to take place. In this simple example, a short message ("Mouse clicked on the button") is displayed to the console.

The hitTestState property is perhaps the most interesting of the button's state properties. You'll notice that the preceding code sets the hitTestState to be the same display object that defines the upState. It is typical to do this because buttons should be activated when the user's mouse is within the bounds of the upState display object.

Although the hitTestState is never visible, failure to set the hitTestState to a display object results in a button that can't be interacted with. Always remember to set the hitTestState of your SimpleButton, even if you simply set it to the same value as upState.


The hitTestState can be set to any display when you'd like to control the active bounds of a button. To create a larger hit area for the button, try modifying the previous code segment to set the hitTestState via this line:

button.hitTestState = createCircle( 0x000000, 50 );

When running this example, you'll notice that the button displays the white "over" circle before the mouse even enters the area of the green circle, contrary to previous behavior. This is because the hit area was increased to a circle of radius 50, giving a larger target area for the user's mouse. You might also notice that black (0x000000) was specified as the color for the hit area circle. This was done on purpose to reinforce the fact that the hit area display object is never visible.

An alternate approach to creating a SimpleButton and setting the four display states for every button is to create a subclass of SimpleButton that defines your button's visual style and creates instances of that instead. Recipe 6.4 describes how to create new visual classes. Following this technique, you can create your own version of a SimpleButton, making it easier to add multiple buttons to your movie.

The following code creates a new RectangleButton class. The RectangleButton class defines the behavior for a special type of SimpleButton that draws a green rectangle with some text on top of it:

package {
  import flash.display.*
  import flash.text.*;
  import flash.filters.DropShadowFilter;

  public class RectangleButton extends SimpleButton {
    // The text to appear on the button
    private var _text:String;
    // Save the width and height of the rectangle
    private var _width:Number;
    private var _height:Number;
    
    public function RectangleButton( text:String, width:Number, height:Number ) {
      // Save the values to use them to create the button states
      _text = text;
      _width = width;
      _height = height;
      
      // Create the button states based on width, height, and text value
      upState = createUpState(  );
      overState = createOverState(  );
      downState = createDownState(  );
      hitTestState = upState;
    }
  
    // Create the display object for the button's up state
    private function createUpState(  ):Sprite {
      var sprite:Sprite = new Sprite(  );
      
      var background:Shape = createdColoredRectangle( 0x33FF66 );
      var textField:TextField = createTextField( false );
          
      sprite.addChild( background );
      sprite.addChild( textField );

      return sprite;
    }
    
    // Create the display object for the button's up state
    private function createOverState(  ):Sprite {
      var sprite:Sprite = new Sprite(  );
      
      var background:Shape = createdColoredRectangle( 0x70FF94 );
      var textField:TextField = createTextField( false );
            
      sprite.addChild( background );
      sprite.addChild( textField );

      return sprite;
    }
    
    // Create the display object for the button's down state
    private function createDownState(  ):Sprite {
      var sprite:Sprite = new Sprite(  );
      
      var background:Shape = createdColoredRectangle( 0xCCCCCC );
      var textField:TextField = createTextField( true );
      
      sprite.addChild( background );
      sprite.addChild( textField );
      
      return sprite;
    }
    
    // Create a rounded rectangle with a specific fill color
    private function createdColoredRectangle( color:uint ):Shape {
      var rect:Shape = new Shape(  );
      rect.graphics.lineStyle( 1, 0x000000 );
      rect.graphics.beginFill( color );
      rect.graphics.drawRoundRect( 0, 0, _width, _height, 15 );
      rect.graphics.endFill(  );
      rect.filters = [ new DropShadowFilter( 2 ) ];
      return rect;
    }
    
    // Create the text field to display the text of the button
    private function createTextField( downState:Boolean ):TextField {
      var textField:TextField = new TextField(  );
      textField.text = _text;
      textField.width = _width;
            
      // Center the text horizontally
      var format:TextFormat = new TextFormat(  );
      format.align = TextFormatAlign.CENTER;
      textField.setTextFormat( format );
      
      // Center the text vertically
      textField.y = ( _height - textField.textHeight ) / 2;
      textField.y -= 2;  // Subtract 2 pixels to adjust for offset
      
      // The down state places the text down and to the right
      // further than the other states
      if ( downState ) {
        textField.x += 1;
        textField.y += 1;
      }
      
      return textField;
    }
  }
}

Because all of the button drawing is encapsulated into its own reusable class, creating new button instances is much easier. Instead of having to create a SimpleButton and define the button states by hand for each instance, you can simply create a new RectangleButton instance and add that to the display list.

The following example shows how to create three different rectangular buttons using this new instance:

package {
  import flash.display.*;
  public class SimpleButtonDemo extends Sprite {
    public function SimpleButtonDemo(  ) {
      
      // Create three rectangular buttons with different text and 
      // different sizes, and place them at various locations within 
      // the movie
      
      var button1:RectangleButton = new RectangleButton( "Button 1", 60, 100 );
      button1.x = 20;
      button1.y = 20;
      
      var button2:RectangleButton = new RectangleButton( "Button 2", 80, 30 );
      button2.x = 90;
      button2.y = 20;
      
      var button3:RectangleButton = new RectangleButton( "Button 3", 100, 40 );
      button3.x = 100;
      button3.y = 60;
      
      // Add the buttons to the display list so they appear on-screen
      addChild( button1 );
      addChild( button2 );
      addChild( button3 );
    }
  }
} 

See Also

Recipes 1.5, 6.1, 6.4, and 6.8


Previous Page
Next Page
Converted from CHM to HTML with chm2web Pro 2.85 (unicode)