Apple Developer Connection Technical: Java
Membership Technical Business Sitemap Log In
★ wanayoo — archive 1999 http://developer.apple.com/java/javatutorial/imagebutton2.htmlNouvelle recherche | Portail wanayoo
Previous document

Step 3 - Registering the Action Listener

Now that we have methods that can respond to mouse events, we need to register our listener with the ImageButton class. This is done in the constructor.


public ImageButton( )
{
    //REGISTER_LISTENERS
    //Insert "ImageButton register listener

Locate the ImageButton register listener clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


public ImageButton( )
{
    //REGISTER_LISTENERS
    //Insert "ImageButton register listener
    Mouse aMouse = new Mouse( );
    this.addMouseListener(aMouse);

First, we create a new instance of our Mouse inner class we defined in Step 2. Secondly, we register the Mouse class as a listener for the ImageButton. Now, when the user performs any mouse movement relating to the ImageButton, the Mouse class will be called to respond to the generated event.

To complete our constructor, we have some additional initialization to perform:


    Mouse aMouse = new Mouse( );
    this.addMouseListener(aMouse);
    //Initialize state information
    //Insert "ImageButton init state"

Locate the ImageButton init state clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


    Mouse aMouse = new Mouse( );
    this.addMouseListener(aMouse);

//Initialize state information //Insert "ImageButton init state"
    imageHash = new Hashtable( );
    actionCommand = "ImageButton Action";

We allocate a new hashtable to contain the button images, and then we initialize our action command string. The action command string will allow objects which receive the action event from our button to determine the source of the message.


Back to top

Step 4 - Handling MouseReleased Messages

We have defined our inner class that handles mouse events and registers that class as a mouseListener for the button. Now it is time to start implementing the methods.


/**
 * Gets called when the mouse button is pressed on this button.
 * @param isMouseInside, if true, the mouse is located inside 
 * the button area, if false the mouse is outside the button 
 * area.
 */
 protected void handleMouseRelease(Boolean isMouseInside)
 {
     //Handle firing an ActionEvent to our listeners if the 
     //mouse was released inside the button.
     //Insert "ImageButton handleMouseReleased"

As you can see from the JavaDoc, the handleMouseRelease( ) method gets called when the user presses the mouse button on this button and then releases it. We explored the mechanism for propagating this message in Step 2. We take a Boolean parameter that lets us know if the mouse was inside the button when it was released.

Locate the ImageButton handleMouseReleased clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


/**
 * Gets called when the mouse button is pressed on this button.
 * @param isMouseInside, if true, the mouse is located inside 
 * the button area, if false the mouse is outside the button 
 * area.
 */
 protected void handleMouseRelease(Boolean isMouseInside)
 {
     //Handle firing an ActionEvent to our listeners if the 
     //mouse was released inside the button.
     //Insert "ImageButton handleMouseReleased"
     if (isMouseInside)
          fireActionEvent( );
}

We check to see if the mouse was still inside the button when it was released. The Boolean isMouseInside is passed to us by ImageButton_MouseReleased( ) from Step 2. If the mouse is not inside, we don’t do anything. Otherwise, we call fireActionEvent( ), which creates a new action event and notifies any registered listeners of the event. We will talk about this function in more detail in Step 10. For now, it is only important to know that this function will notify other slideshow components that the button has been pressed so that they have a chance to respond to this action.


Back to top

Step 5 - Implementing addImage( )

Skipping down past the abstract declarations of handleRollover( ) and handleMousePressed( ), which are implemented in RolloverButton, we come to the declaration of addImage:


/**
 * Adds an image to the button.
 * @param imagePath, the location of the image resource to use.
 * This path is relative to the location of this class file.
 * @param imageName, the name used to identify the image for 
 * later use in this button.
 * @see #removeImage
 */
 public void addImage(String imagePath, String imageName)
 {
    //Handle storing the information in our internal data 
    //structure.
    //Insert "ImageButton addImage"

Addimage is used to add an image to the button’s list of usable images. It takes an imagePath as a string which is a location and name of the image file to use relative to the application resources, and a string that specifies the name of the image. This is not the filename. It is used to internally refer to that particular image.

Locate the ImageButton addImage clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


/**
 * Adds an image to the button.
 * @param imagePath, the location of the image resource to use.
 * This path is relative to the location of this class file.
 * @param imageName, the name used to identify the image for 
 * later use in this button.
 * @see #removeImage
 */
 public void addImage(String imagePath, String imageName)
 {
    //Handle storing the information in our internal data 
    //structure.
    //Insert "ImageButton addImage"
    if (imageName != null && !imageName.equals(""))
    {
        Image newImage = Misc.loadImage(imagePath, this, true);
        if (newImage != null)
        {
            imageHash.put(imageName, newImage);
        }
     }
}

This method checks the imageName to make sure that it is neither null, nor empty. Since we are going to store the image in a hashtable and use the name as a key, the name must not be null and it must be non-empty. If the imageName does not meet these criteria, we exit the function (drop out of the if statement). Otherwise, we load the image using a supplementary routine from the Misc class and store it in a temporary variable. The Misc class has a single routine that loads images and does error handling. Its function is outside the scope of this tutorial, but we felt it was important to include a reasonably robust mechanism for loading resources that you may use in your own projects.

If the image was loaded successfully (i.e., the image loaded is not null), we add the item to our hashtable, using the image name as the key and the image as the data. What is a hashtable? A hashtable is a data structure that allows you to store data in several storage slots retrievable by a key. The key is used to determine which slot the item is stored in. It is a very fast and efficient storage mechanism which is built-in to java.

Now that we have a mechanism for adding images to our pool of button images, we need to be able to remove them.


Back to top

Step 6 - Implementing removeImage( )

The removeImage function can be used to remove unwanted images from the button image pool, or for cleanup purposes.


/**
 * Removes an image from the button
 * @param imageName, the identifying name of the image to remove.
 * @see #addImage
 */
 public void removeImage(String imageName)
 {
     //Handle removing the image from our internal data 
     //structure.
     //Insert "ImageButton removeImage"

This method only takes a string as a parameter. It takes the imageName, looks it up in the hashtable, and deletes the item if it is found.

Locate the ImageButton removeImage clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


/**
 * Removes an image from the button
 * @param imageName, the identifying name of the image to remove.
 * @see #addImage
 */
 public void removeImage(String imageName)
 {
     //Handle removing the image from our internal data 
     //structure.
     //Insert "ImageButton removeImage"
     if (imageName != null && !imageName.equals(""))
     {
         imageHash.remove(imageName);
     }
}

The body of this method is fairly simple. We check to see if the name passed to the function is non-empty and non-null, and then call remove from the hashtable with the image name as the parameter. Now it’s time to look at setImage( ).

Back to top

Step 7 - Implementing setImage( )

The routine setImage( ) is used to change the image displayed in the button to a specific image that has been added to the collection of button images.


/**
 * Sets the image for the button to use as its current image.
 * @param imageName, the identifying name of the image to use.
 */
 public void setImage(String imageName)
 {
     //Handle locating the image in our internal data structure,
     //setting it as the current image, and repainting the 
     //button.
     //Insert "ImageButton setImage"

Locate the ImageButton setImage clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


//**
 * Sets the image for the button to use as its current image.
 * @param imageName, the identifying name of the image to use.
 */
 public void setImage(String imageName)
 {
     //Handle locating the image in our internal data structure,
     //setting it as the current image, and repainting the 
     //button.
     //Insert "ImageButton setImage"
     if (imageName != null && !imageName.equals(""))
     {
         Image temp = (Image)imageHash.get(imageName);
         if (temp != null)
         {
              image = temp;
              this.imageName = imageName;
              repaint( );
         }
     }
}

SetImage( ) seems a little more difficult on the surface than removeImage( ), but it is really not. We check to make sure that the image name is neither null nor empty, and then retrieve the current image from the hashtable, storing it in the temporary variable temp. After checking to make sure that the retrieved image is not null, we set our image data member to the retrieved image. At first glance, this may seem strange. Why are we using a temporary variable in the first place? Why couldn’t we write:

image = (Image)imageHash.get(imageName);

and then check to see if image is null? Well then if the image we were loading did not exist, we would have no idea what the image variable previously contained, and our current image would be null. This would be a bad idea. So we retrieve the image into a temporary variable, and then if it is valid, set the current image variable to the temporary. Then we store the image name:

this.imageName = imageName;

What’s up with the this.imageName? Well, you may note that the parameter of this routine is called imageName. Since we want to set the value of the ImageButton data member imageName to the local routine parameter imageName, we use this.imageName to specify class scope for the variable instead of local scope.

Last but not least, we call repaint( ), a java.awt.Component method that redraws the image button and displays our new image. Whew! Now it’s time for the trivial getImage( ) method.

Back to top

Step 8 - Implementing getImage( )

This method quite simply returns the name of the current image.


/**
 * Gets the name of the image currently in use.
 * @return The identifying name of the image being used.
 */
 public String getImage( )
 {
     //Return the current image name.
     //Insert "ImageButton getImage"

Locate the ImageButton getImage clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


/**
 * Gets the name of the image currently in use.
 * @return The identifying name of the image being used.
 */
 public String getImage( )
 {
     //Return the current image name.
     //Insert "ImageButton getImage"
     return imageName;
}

It really doesn’t get much easier than this. We simply return our current image name stored in the image button data member imageName. Next is the very similar function getImageObject( ).

Back to top

Step 9 - Implementing getImageObject( )

This method returns the actual image object associated with the current button image, not just the name.


/**
 * Gets the actual Image Object which is currently being used.
 * @return The java.awt.Image currently in use.
 */
 public Image getImageObject( )
 {
     //Return the current image object.
     //Insert "ImageButton getImageObject"

Locate the ImageButton getImageObject clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


/**
 * Gets the actual Image Object which is currently being used.
 * @return The java.awt.Image currently in use.
 */
 public Image getImageObject( )
 {
     //Return the current image object.
     //Insert "ImageButton getImageObject"
     return image;
}

This should come as no surprise. We simply return our current image stored in our image data member of ImageButton. Now that we can add, remove, set and get button images, it is time to implement some routines for responding to button actions.

Back to top

Step 10 - Handling Action Events

As we recall from Step 2 and Step 3, there is a very specific chain of events that occur when the user clicks on the button. The first thing that happens is our MouseHandler inner class gets called along with the appropriate MouseEvent. In the case of a mouse click, our mousePressed( ) routine gets called followed by mouseReleased( ). If the mouse is still inside of the button when it is released, we call fireActionEvent( ). This sends messages to other components (that are registered as listeners for the button) to notify them that the button was activated.


public Image getImageObject( )
{
    //Return the current image object.
    //Insert "ImageButton getImageObject"
    return image;
}
//Routines for handling ActionListener management.
//Insert "ImageButton Action Management"

Let’s look at the mechanism for action management. Locate the ImageButton Action Management clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


//Routines for handling ActionListener management.
//Insert "ImageButton Action Management"
/**
 * Sets the command name of the action event fired by this 
 * button.
 * @param command The name of the action event command fired 
 * by this button 
 */
 public void setActionCommand(String command)
 {
     actionCommand = command;
 }

/**
 * Returns the command name of the action event fired by this 
 * button.
 * @return the action command name
 */
 public String getActionCommand( )
 {
     return actionCommand;
 }

/**     
 * Adds the specified action listener to receive action events
 * from this button.
 * @param l the action listener
 */
 public void addActionListener(ActionListener l)
 {
     actionListener = AWTEventMulticaster.add(actionListener, l);
 }

/**
 * Removes the specified action listener so it no longer receives
 * action events from this button.
 * @param l the action listener
 */
 public void removeActionListener(ActionListener l)
 {
     actionListener = AWTEventMulticaster.remove(
                                 actionListener, l);
 }
	
/**
 * Fire an action event to the listeners.
 */
 protected void fireActionEvent( )
 {
     if (actionListener != null)
         actionListener.actionPerformed(new ActionEvent(this, 
               ActionEvent.ACTION_PERFORMED, actionCommand));
 }

These methods encapsulate a mechanism for broadcasting notification that our button was pressed. This notification takes place in the form of an action event. Let’s look at these functions one at a time.


public void setActionCommand(String command) 
{ 
    actionCommand = command;          
} 

When an ActionEvent is sent, it contains a string called an action command. This command gives the receiver additional information about what the command is. This routine is used to define the current action command to be sent out by the button. The code simply caches the action command to our data member.


public String getActionCommand( ) 
{ 
    return actionCommand;          
} 

This routine retrieves the current action command by returning the contents of our actionCommand data member.


public void addActionListener(ActionListener l) 
{ 
    actionListener = AWTEventMulticaster.add(actionListener, l); 
} 

This routine allows Listener objects interested in receiving ActionEvents from this button to register themselves with the button.


public void removeActionListener(ActionListener l) 
{ 
    actionListener = AWTEventMulticaster.remove( actionListener, l); 
}

This allows previously interested Listeners to tell the button they no longer need to be notified when an ActionEvent is generated by this button.


protected void fireActionEvent( ) 
{ 
    if (actionListener != null) 
        actionListener.actionPerformed(new
        ActionEvent(this,
        ActionEvent.ACTION_PERFORMED, actionCommand)); 
}

This calls the actionPerformed method of all the registered listeners with a new action event describing the details of the event, effectively broadcasting the action event to all interested Listeners.

Now it’s time to implement getPreferredSize( ).

Back to top

Step 11 - Implementing getPreferredSize( )

Because our button selects images from an image pool, we don’t know at design time how big to make the button. Thus, we implement a getPreferredSize method. This method will be called by the layout manager of our container in order to calculate the button size. We need to return a size based on the size of the image we are using.


/** 
 * Returns the preferred size of this component.
 * @see #getMinimumSize
 * @see LayoutManager
 */
 public Dimension getPreferredSize( )
 {
     //If the current image is not null, then return the size of 
     //the image.
     //If it is null, defer to the super class.
     //Insert "ImageButton getPreferredSize"

We are overriding the getPreferredSize( ) method from java.awt.Component. It returns a Dimension object which specifies the preferred height and width of our button. Locate the ImageButton getPreferredSize clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


/** 
 * Returns the preferred size of this component.
 * @see #getMinimumSize
 * @see LayoutManager
 */
 public Dimension getPreferredSize( )
 {
     //If the current image is not null, then return the size of 
     //the image. If it is null, defer to the super class.
     //Insert "ImageButton getPreferredSize"
     if (image != null)
         return new Dimension(image.getWidth(this), 
                    image.getHeight(this));
    	
     return super.getPreferredSize( );
}

We want to return the size of our current image as the preferred size of the button. The first thing we do is check to see if the image is null. If it is, we call getPreferredSize( ) from our superclass so that we can use the default component behavior. Otherwise, we return a new Dimension object that we create using the height and width of our image object.

We are almost finished with this class. The only thing that remains is drawing our button. This is done in the paint method.

Back to top

Step 12 - Implementing paint( )

Paint( ) is the routine that gets called to draw our object on the screen.


/** 
 * Paints the component. This method is called when the contents
 * of the component should be painted in response to the 
 * component first being shown or damage needing repair. The 
 * clip rectangle in the Graphics parameter will be set to the 
 * area which needs to be painted.
 * @param g the specified Graphics window
 * @see #update
 */
 public void paint(Graphics g)
 {
    //Let the super class draw, then handle drawing the current 
    //image.
    //Insert "ImageButton paint"

As you can see from the JavaDoc, the paint( ) method is called when the contents of the component needs to be drawn due to invalidation of the component or a request for an update. The Graphics parameter g is the graphics context the object needs to be drawn in. Locate the ImageButton paint clipping in the ImageButton folder and drag it directly below the last line of code shown above. Your code should now look like this:


/** 
 * Paints the component. This method is called when the contents
 * of the component should be painted in response to the 
 * component first being shown or damage needing repair. The 
 * clip rectangle in the Graphics parameter will be set to the 
 * area which needs to be painted.
 * @param g the specified Graphics window
 * @see #update
 */
 public void paint(Graphics g)
 {
    //Let the super class draw, then handle drawing the current 
    //image.
    //Insert "ImageButton paint"
    super.paint(g);

    if (image != null)
        g.drawImage(image, 0, 0, this);
}

First, we call the paint method of our base class to insure that any preparatory imaging occurs. Then we check to see if the image is null. If it is not, we draw the current image starting at location 0, 0. This means that we draw the image so that the top left corner is 0 pixels from the top of the button bounds, 0 pixels from the left of the button bounds, and we use the default image dimensions. That’s all there is to it!

Back to top

Summary

In review, we set up our class to be derived from Component. This allows us to inherit some basic functionality such as being able to draw to the screen, having a bounds, etc. We set up an interface that derived classes will implement to do things like respond to action events. We set up a MouseListener and registered it with our button so that we can respond to mouse events such as MousePressed, MouseReleased, MouseEntered, and MouseExited. We wrote an inner class to send action events so that our derived classes can respond appropriately to user interaction, and we laid some groundwork for our derived classes such as several image routines for getting, setting, adding and removing images. We wrote a preferredSize method so we can tell layout managers how big we want to be, and we added a paint method so that we could draw ourselves.

That may seem like a lot of work, but a lot of it is to simplify the creation of our derived classes which for the most part are much more simple than this class. We have implemented the core functionality for our button, and the road is now much easier from here.

Now we are ready to go back to our main tutorial file and prepare for the next step, Building the Rollover button.

Previous Page