Solution for
Programming Exercise 6.2


THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.

Exercise 6.2: Improve your dice applet from the previous exercise so that it also responds to keyboard input. When the applet has the input focus, it should be hilited with a colored border, and the dice should be rolled whenever the user presses a key on the keyboard. This is in addition to rolling them when the user clicks the mouse on the applet. Here is an applet that solves this exercise:


Discussion

The modifications that have to be made to the program from the previous exercise are fairly straightforward. The applet has to handle key presses, so it must implement the KeyListener interface, and it must register itself to listen for key events by calling dice.addKeyListener(this) in its init() method. (In fact it is actually the drawing surface component, dice, that gets the input focus and generates key events.) The keyPressed() method just has to roll the dice, by calling the same roll() subroutine that is used by the mousePressed() routine.

The border of the applet changes from blue to red when the applet has the input focus. In order to implement this, the applet must process focus events. So it needs to implement the FocusListener interface and register itself to listen for focus events from the dice component. A boolean-valued instance variable, focused, is set to true when the applet gains the focus and to false when the applet loses the focus. The color that is used for drawing the border depends on the value of this variable. This processing of focus events is identical to what was outlined in Section 6.5.

There is one thing that is easy to forget: , A JPanel does not automatically receive the input focus when the user clicks on it. The focus must be requested by calling the requestFocus() method. This is done in the mousePressed() routine, in response to a mouse click on the applet. (If you forget this, your applet might actually still work on some platforms, but on others it will never get the input focus and the keyboard will never work.)

In the following solution, modifications to the solution for Exercise 6.1 are shown in red.


The Solution

    /*
       Shows a pair of dice that are rolled when the user clicks on the
       applet or if the applet has the input focus and the user presses 
       any key.  It is assumed that the applet is 100-by-100 pixels.
       A clicking sound is played when the dice are rolled.
    */

    
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class ClickableDice2 extends JApplet 
                     implements MouseListener, FocusListener, KeyListener {
    
       int die1 = 4;  // The values shown on the dice.
       int die2 = 3;
       
       boolean focused;  // This is true when the applet has the
                         // input focus.  It's value is managed by
                         // the focusGained() and focusLost() methods.
       
       DiceCanvas dice;  // An object belonging to the nested class
                         // DiceCanvas, which is used as the drawing
                         // surface on which the dice are displayed.
    
       public void init() {
              // To initialize the applet, create the drawing surface
              // object and use it as the applet's content pane.
              // Set the applet to listen for events from the DiceCanvas,
              // and set the background color of the DiceCanvas.
          dice = new DiceCanvas();
          setContentPane(dice);
          dice.addMouseListener(this);
          dice.addFocusListener(this);
          dice.addKeyListener(this);
          dice.setBackground( new Color(200,200,255) );  // light blue
       }
       
    
       void drawDie(Graphics g, int val, int x, int y) {
             // Draw a die with upper left corner at (x,y).  The die is
             // 35 by 35 pixels in size.  The val parameter gives the
             // value showing on the die (that is, the number of dots).
          g.setColor(Color.white);
          g.fillRect(x, y, 35, 35);
          g.setColor(Color.black);
          g.drawRect(x, y, 34, 34);
          if (val > 1)  // upper left dot
             g.fillOval(x+3, y+3, 9, 9);
          if (val > 3)  // upper right dot
             g.fillOval(x+23, y+3, 9, 9);
          if (val == 6) // middle left dot
             g.fillOval(x+3, y+13, 9, 9);
          if (val % 2 == 1) // middle dot (for odd-numbered val's)
             g.fillOval(x+13, y+13, 9, 9);
          if (val == 6) // middle right dot
             g.fillOval(x+23, y+13, 9, 9);
          if (val > 3)  // bottom left dot
             g.fillOval(x+3, y+23, 9, 9);
          if (val > 1)  // bottom right dot
             g.fillOval(x+23, y+23, 9,9);
       }
    
    
       void roll() {
             // Roll the dice by randomizing their values.  Tell the
             // system to repaint the applet, to show the new values.
             // Also, play a clicking sound to give the user more feedback.
          die1 = (int)(Math.random()*6) + 1;
          die2 = (int)(Math.random()*6) + 1;
          play(getCodeBase(), "click.au");
          dice.repaint();
       }
       
       
       public void focusGained(FocusEvent evt) {
             // This is called when the dice canvas gets the input focus
           focused = true;  // Record the fact that we have focus.
           dice.repaint();  // Redraw with a red border.
       }
       
    
       public void focusLost(FocusEvent evt) {
             // This is called when the dice canvas loses the input focus
           focused = false;  // Record the fact that we don't have focus.
           dice.repaint();   // Redraw with a blue border.
       }
       
    
       public void mousePressed(MouseEvent evt) {
             // When the user clicks the applet, roll the dice.
             // Also, request the input focus.
          roll();
          dice.requestFocus();
       }
       
       
       public void keyPressed(KeyEvent evt) {
             // When the user presses any key (and the applet has
             // the input focus), roll the dice.
          roll();
       }
       
    
       public void mouseReleased(MouseEvent evt) { }  // Required for the
       public void mouseClicked(MouseEvent evt) { }   //    MouseListener
       public void mouseEntered(MouseEvent evt) { }   //       interface.
       public void mouseExited(MouseEvent evt) { }
       
       public void keyReleased(KeyEvent evt) { }  // Required for the
       public void keyTyped(KeyEvent evt) { }     //   KeyListener interface.
       

       class DiceCanvas extends JPanel {
            // A nested class to represent the drawing surface
            // on which the dice are displayed.
          public void paintComponent(Graphics g) {
                 // The paint method draws a border and then
                 // draws the two dice.  The border is red when
                 // the applet has focus and is blue otherwise.
             super.paintComponent(g);  // fill with background color.
             if (focused)
                g.setColor( Color.red);
             else
                g.setColor( Color.blue );
             g.drawRect(0,0,99,99);
             g.drawRect(1,1,97,97);
             drawDie(g, die1, 10, 10);
             drawDie(g, die2, 55, 55);
          }
       }
    
    } // end class ClickableDice2


[ Exercises | Chapter Index | Main Index ]