Module 11: AWT Part II: GUI Components


An overview of AWT's GUI components

For GUI-building, here are some of the more important "standard" components:

Each component can generate events and therefore has an associated event-listener.

In the sequel, we will learn how to create some of these components and handle events related to them.

In-Class Exercise 11.1: Download this file, compile and execute it. It should produce a form. Try to identify which of the above components are being used. Below, we will learn to build this form step-by-step.


Setting up a form with a CardLayout

The program above used a CardLayout to place a number of panels on behind another.

As a first step in constructing the form, we will do the following:

As usual, we will create a frame and use it as follows:


class NewFrame extends Frame {

  // Constructor. 
  public NewFrame (int width, int height)
  {
  }

  // ... other stuff ... 

}

public class Survey {
  public static void main (String[] argv)
  {
    NewFrame nf = new NewFrame (500, 300);
  }
}
  

We will place the code for creating the top panel (instructions), centerpanel (questions) and bottom panel (buttons) in the constructor.

But because the questions are large (a lot of code), we will call methods to handle individual questions.

Here is the constructor:


class NewFrame extends Frame {

  // Data. 
  Panel centerpanel;   // For the questions. 
  CardLayout card;     // For the centerpanel. 

  // Constructor. 
  public NewFrame (int width, int height)
  {
    this.setTitle ("Snoot Club Membership Test");
    this.setResizable (true);
    this.setBackground (Color.cyan);
    this.setSize (width, height);
    // this.setLayout (new BorderLayout()); 

    // First, the instruction line at the top. 
    Label L = new Label ("Are you elitist enough for our exclusive club?"
                         + " Fill out the form and find out");
    L.setFont (new Font ("Serif", Font.BOLD | Font.ITALIC, 15));
    L.setForeground (Color.blue);
    this.add (L, BorderLayout.NORTH);

    // Next, a panel of four buttons at the bottom. 
    // The four buttons: quit, submit, next-question, previous-question. 
    Panel bottom_panel = get_bottom_panel ();
    this.add (bottom_panel, BorderLayout.SOUTH);

    // Now, the center panel with the questions. 
    centerpanel = new Panel ();

    // Set the layout to a CardLayout. 
    card = new CardLayout ();    
    centerpanel.setLayout (card);

    // Each question will be created in a separate method as 
    // a panel. Note that the cardlayout requires a label as  
    // second parameter. 
    Panel p = first_question ();
    centerpanel.add (p, "1");

    p = second_question ();
    centerpanel.add (p, "2");
  
    p = third_question ();
    centerpanel.add (p, "3");

    p = fourth_question ();
    centerpanel.add (p, "4");

    // Now add the centerpanel to the frame. 
    this.add (centerpanel, BorderLayout.CENTER);

    // Finally, show the frame. 
    this.setVisible (true);
  }

  // No-parameter constructor. 
  public NewFrame () 
  {
    this (500, 300);
  }


  Panel get_bottom_panel ()
  {
    // Create and place buttons into a panel. 
  }

}
  

Note:

Here is the complete source: (source file)


import java.awt.*;
import java.awt.event.*;


class NewFrame extends Frame {

  // Data. 
  Panel centerpanel;   // For the questions. 
  CardLayout card;     // For the centerpanel. 

  // Constructor. 
  public NewFrame (int width, int height)
  {
    this.setTitle ("Snoot Club Membership Test");
    this.setResizable (true);
    this.setBackground (Color.cyan);
    this.setSize (width, height);
    // this.setLayout (new BorderLayout()); 

    // First, the instruction line at the top. 
    Label L = new Label ("Are you elitist enough \nfor our exclusive club?"
                         + " Fill out the form and find out");
    L.setFont (new Font ("Serif", Font.BOLD | Font.ITALIC, 15));
    L.setForeground (Color.blue);
    this.add (L, BorderLayout.NORTH);

    // Next, a panel of four buttons at the bottom. 
    // The four buttons: quit, submit, next-question, previous-question. 
    Panel bottom_panel = get_bottom_panel ();
    this.add (bottom_panel, BorderLayout.SOUTH);

    // Now, the center panel with the questions. 
    centerpanel = new Panel ();

    // Set the layout to a CardLayout. 
    card = new CardLayout ();    
    centerpanel.setLayout (card);

    // Each question will be created in a separate method as 
    // a panel. Note that the cardlayout requires a label as  
    // second parameter. 
    Panel p = first_question ();
    centerpanel.add (p, "1");

    p = second_question ();
    centerpanel.add (p, "2");
  
    p = third_question ();
    centerpanel.add (p, "3");

    p = fourth_question ();
    centerpanel.add (p, "4");

    // Now add the centerpanel to the frame. 
    this.add (centerpanel, BorderLayout.CENTER);

    // Finally, show the frame. 
    this.setVisible (true);
  }

  // No-parameter constructor. 
  public NewFrame () 
  {
    this (500, 300);
  }


  Panel get_bottom_panel ()
  {
    // Create a panel into which we will place buttons. 
    Panel bottom_panel = new Panel ();

    // A "previous-question" button. 
    Button backward = new Button ("Previous question");
    backward.setBackground (Color.green);
    backward.setFont (new Font ("Serif", Font.PLAIN | Font.BOLD, 15));
    backward.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  // Go back in the card layout. 
	  card.previous (centerpanel);
	}  
      }
    );
    bottom_panel.add (backward);

    // A forward button. 
    Button forward = new Button ("Next question");
    forward.setBackground (Color.green);
    forward.setFont (new Font ("Serif", Font.PLAIN | Font.BOLD, 15));
    forward.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  // Go forward in the card layout. 
	  card.next (centerpanel);
	}  
      }
    );
    bottom_panel.add (forward);

    // A submit button. 
    Button submit = new Button ("Submit");
    submit.setBackground (Color.green);
    submit.setFont (new Font ("Serif", Font.PLAIN | Font.BOLD, 15));
    submit.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  // Perform submit task. 
	  compute_result();
	}  
      }
    );
    bottom_panel.add (submit);

    Button quitb = new Button ("Quit");
    quitb.setBackground (Color.red);
    quitb.setFont (new Font ("Serif", Font.PLAIN | Font.BOLD, 15));
    quitb.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  System.exit (0);
	}  
      }
    );
    bottom_panel.add (quitb);

    return bottom_panel;
  }


  // This method will handle the panel for the first question. 

  Panel first_question ()
  {
    // We will package everything into a panel and return the panel. 
    Panel subpanel = new Panel ();

    // For now, we'll just create a label. 
    Label L1 = new Label ("Question 1:");
    subpanel.add (L1);

    return subpanel;
  }


  // Second question. 

  Panel second_question ()
  {
    Panel subpanel = new Panel ();

    Label L1 = new Label ("Question 2:");
    subpanel.add (L1);

    return subpanel;
  }


  // Third question. 

  Panel third_question ()
  {
    Panel subpanel = new Panel ();

    Label L1 = new Label ("Question 3:");
    subpanel.add (L1);

    return subpanel;
  }


  // Fourth question. 

  Panel fourth_question ()
  {
    Panel subpanel = new Panel ();

    Label L1 = new Label ("Question 4:");
    subpanel.add (L1);

    return subpanel;
  }


  // This method is called after submit is pressed. 

  void compute_result ()
  {
    // To be filled in ... 
  }

}


public class Survey0 {
  public static void main (String[] argv)
  {
    NewFrame nf = new NewFrame (500, 300);
  }
}
  

Note:

When executed, the result looks like this:
Window

In-Class Exercise 11.2: Improve the font of each label in the four questions.


Using a TextField for user input (Question 1)

For Question 1, the user needs to enter of the numbers 1,2,3 or 4.

To allow a simple one-line input from the user, a TextField is best.

Here is the code for Question1: (source file)


  // This method will handle the panel for the first question. 

  TextField tf;        // Used in question 1. 
  double q1_score = 0; // Score on question 1. 

  Panel first_question ()
  {
    // We will package everything into a panel and return the panel. 
    Panel subpanel = new Panel ();

    // We will place things in a single column, so  
    // a GridLayout with one column is appropriate. 
    subpanel.setLayout (new GridLayout (8,1));

    Label L1 = new Label ("Question 1:");
    L1.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L1);

    Label L2 = new Label ("  Select a vacation destination");
    L2.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L2);

    Label L3 = new Label ("    1. Baltimore");
    L3.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L3);

    Label L4 = new Label ("    2. Disneyland");
    L4.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L4);

    Label L5 = new Label ("    3. Grand Canyon");
    L5.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L5);

    Label L6 = new Label ("    4. French Riviera");
    L6.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L6);

    Label L7 = new Label ("Enter 1,2,3 or 4 below:");
    L7.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L7);

    // Here's the textfield to get user-input. 
    tf = new TextField ();
    tf.addTextListener (
      new TextListener () {
        // This interface has only one method. 
        public void textValueChanged (TextEvent t)
	{
	  String q1_string = tf.getText();
	  if (q1_string.equals ("2"))
	    q1_score = 2;
	  else if (q1_string.equals ("3"))
	    q1_score = 3;
	  else if (q1_string.equals ("4"))
	    q1_score = 4;
	  else
	    q1_score = 1;
	}  
      }
    );
    subpanel.add (tf);

    return subpanel;
  }
  

When executed, Question 1 looks like this:
Window

Note:

  • We used a Label for each line of output text, e.g.,
    
        Label L5 = new Label ("    3. Grand Canyon");
        L5.setFont (new Font ("SansSerif", Font.ITALIC, 20));
        subpanel.add (L5);
        
  • A TextField uses a TextListener, which requires implementing the textValueChanged() method:
    
        tf.addTextListener (
          new TextListener () {
            // This interface has only one method. 
            public void textValueChanged (TextEvent t)
    	{
    	  String q1_string = tf.getText();
    	  if (q1_string.equals ("2"))
    	    q1_score = 2;
    	  else if (q1_string.equals ("3"))
    	    q1_score = 3;
    	  else if (q1_string.equals ("4"))
    	    q1_score = 4;
    	  else
    	    q1_score = 1;
    	}  
          }
        );
       
    Here:
    • We use the getText() method of TextField to extract the user's input (as a String).
    • A score is assigned (for our particular application) based on the input.


  • Some useful facts about TextField's:
    • You can set the length of a TextField (using the setColumns() method or the constructor).
    • You can decide to echo any character instead of what the user types in, useful when asking for a password.
      (Typically, the echo character used is a "*").


Using Checkbox's for user input (Question 2)

When the user needs to select one or more items from a known set of items, a collection of Checkbox's is convenient:
Window
(Here, two items have been selected - the first and the third).

Here is the code for Question 2: (source file)


  // For the second question, a collection of checkboxes 
  // will be used. More than one selection can be made. 
  // A listener is required for each checkbox. The state 
  // of each checkbox is recorded. 

  boolean        // Store selections for this question. 
    q2_option1,
    q2_option2,
    q2_option3,
    q2_option4;
  int q2_score;  // Score on question 2.       

  Panel second_question ()
  {
    Panel subpanel = new Panel ();
    subpanel.setLayout (new GridLayout (7,1));

    Label L1 = new Label ("Question 2:");
    L1.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L1);

    Label L2 = new Label ("  Select ONE OR MORE things that ");
    L2.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L2);

    Label L3 = new Label ("  you put into your lunch sandwich");
    L3.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L3);

    // Initialize the selections to false. 
    q2_option1 = q2_option2 = q2_option3 = q2_option4 = false;

    // First checkbox. 
    Checkbox c1 = new Checkbox ("Ham, beef or turkey");
    c1.addItemListener (
      new ItemListener () {
        public void itemStateChanged (ItemEvent i)
	{
	  Checkbox c = (Checkbox) i.getSource();
          q2_option1 = c.getState();
	}
      }
    );
    subpanel.add (c1);

    // Second checkbox. 
    Checkbox c2 = new Checkbox ("Cheese");
    c2.addItemListener (
      new ItemListener () {
        // This is where we will react to a change in checkbox. 
        public void itemStateChanged (ItemEvent i)
	{
	  Checkbox c = (Checkbox) i.getSource();
          q2_option2 = c.getState();
	}
      }
    );
    subpanel.add (c2);

    // Third checkbox. 
    Checkbox c3 = new Checkbox ("Sun-dried Arugula leaves");
    c3.addItemListener (
      new ItemListener () {
        public void itemStateChanged (ItemEvent i)
	{
	  Checkbox c = (Checkbox) i.getSource();
          q2_option3 = c.getState();
	}
      }
    );
    subpanel.add (c3);

    // Fourth checkbox. 
    Checkbox c4 = new Checkbox ("Lemon-enhanced smoked Siberian caviar");
    c4.addItemListener (
      new ItemListener () {
        public void itemStateChanged (ItemEvent i)
	{
	  Checkbox c = (Checkbox) i.getSource();
          q2_option4 = c.getState();
	}
      }
    );
    subpanel.add (c4);

    return subpanel;
  }
  

Note:

  • There are four Checkbox's for the four selections.

  • The text for a Checkbox is passed to the constructor.

  • The Checkbox uses an ItemListener.

  • The itemStateChanged() is the only method in ItemListener.

  • The getState() method of a Checkbox returns true if the box is "clicked on".

  • To call the getState() method, we need a reference to the Checkbox itself, which we can get from the parameter:
    
    	  Checkbox c = (Checkbox) i.getSource();
              q2_option1 = c.getState();
        
  • The following code will NOT compile:
    
              q2_option1 = c1.getState();
        
    (Since c1 is a local variable and not on the heap).


Using a CheckboxGroup to force only one selection (Question 3)

Suppose we want to allow only one Checkbox in a collection to be selected.

This can be achieved by associating a CheckboxGroup instance with the group of Checkbox's.

For example, in Question 3: (source file)


  // The third question allows only one among four choices 
  // to be selected. We will use checkboxes, but also use 
  // an associated CheckboxGroup to enforce only one selection. 

  int q3_score = 0;

  Panel third_question ()
  {
    Panel subpanel = new Panel ();
    subpanel.setLayout (new GridLayout (6,1));

    Label L1 = new Label ("Question 3:");
    L1.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L1);

    Label L2 = new Label ("  And which mustard do you use?");
    L2.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L2);

    // First, create the CheckboxGroup instance. 
    // This instance will be passed to each Checkbox. 
    CheckboxGroup cgroup = new CheckboxGroup();

    // First checkbox. 
    Checkbox c1 = new Checkbox ("Who cares?", cgroup, true);
    c1.addItemListener (
      new ItemListener () {
        public void itemStateChanged (ItemEvent i)
	{
	  Checkbox c = (Checkbox) i.getSource();
          if (c.getState()) q3_score = 1;
	}
      }
    );
    subpanel.add (c1);

    // Second checkbox. 
    Checkbox c2 = new Checkbox ("Safeway Brand", cgroup, false);
    c2.addItemListener (
      new ItemListener () {
        public void itemStateChanged (ItemEvent i)
	{
	  Checkbox c = (Checkbox) i.getSource();
          if (c.getState()) q3_score = 2;
	}
      }
    );
    subpanel.add (c2);

    // Third checkbox. 
    Checkbox c3 = new Checkbox ("Fleishman's", cgroup, false);
    c3.addItemListener (
      new ItemListener () {
        public void itemStateChanged (ItemEvent i)
	{
	  Checkbox c = (Checkbox) i.getSource();
          if (c.getState()) q3_score = 3;
	}
      }
    );
    subpanel.add (c3);

    // Fourth checkbox. 
    Checkbox c4 = new Checkbox ("Grey Poupon", cgroup, false);
    c4.addItemListener (
      new ItemListener () {
        public void itemStateChanged (ItemEvent i)
	{
	  Checkbox c = (Checkbox) i.getSource();
          if (c.getState()) q3_score = 4;
	}
      }
    );
    subpanel.add (c4);

    return subpanel;
  }
  

Note:

  • First, an instance of CheckboxGroup is created:
    
        CheckboxGroup cgroup = new CheckboxGroup();
        
  • This instance is passed to the constructor (a different constructor) of each Checkbox:
    
        Checkbox c1 = new Checkbox ("Who cares?", cgroup, true);
        
    Here, the last boolean indicates the initial state (only one can be true).

Here is the result (the checkbox button looks different - a diamond):
Window


Using a drop-down Choice-list (Question 4)

For Question 4, we want a drop-down list:
Window

Such lists are implemented using a Choice instance: (source file)


  // For the fourth question we will use a drop-down Choice. 

  double q4_score = 0;
  Choice q4_choice;

  Panel fourth_question ()
  {
    Panel subpanel = new Panel ();
    subpanel.setLayout (new GridLayout (3,1));

    Label L1 = new Label ("Question 4:");
    L1.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L1);

    Label L2 = new Label ("  Your movie preference, among these:");
    L2.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L2);

    // Create a Choice widget. 
    q4_choice = new Choice ();
    q4_choice.add ("Lethal Weapon IV");
    q4_choice.add ("Titanic");
    q4_choice.add ("Saving Private Ryan");
    q4_choice.add ("Le Art Movie avec subtitles");
    q4_score = 1;
    q4_choice.addItemListener (
      new ItemListener () {
        public void itemStateChanged (ItemEvent e)
	{
	  q4_score = 1 + q4_choice.getSelectedIndex();
	  System.out.println ("Choice: " + q4_choice.getSelectedIndex());
	}
      }
    );
    subpanel.add (q4_choice);

    return subpanel;
  }
  

Note:

  • The add() method of Choice is used to add items.

  • The listener used is ItemListener, which requires implementation of the itemStateChanged() method.

  • To find out which item has been selected, use the getSelectedIndex() method of the Choice instance.

Now the four questions are done. A few more observations:

  • The calculation of a score is done in the method compute_results (see the complete source).

  • After the calculation, the centerpanel is replaced with the "score".
    (Thus, the questions are removed).

  • To replace, the following steps are used (in compute_results):
    
      void compute_result ()
      {
        // Clear the center panel. 
        centerpanel.removeAll();
    
        // Display a wait-cursor. 
        this.setCursor (new Cursor(Cursor.WAIT_CURSOR));
    
        // Create a new panel to display in the center. 
        Panel subpanel = new Panel (new GridLayout (5,1));
    
        // ... compute scores etc. 
    
        // Now add the new subpanel. 
        centerpanel.add (subpanel, "5");
    
        // Need to mark the centerpanel as "altered" 
        centerpanel.invalidate();
    
        // Go back to original cursor. 
        this.setCursor (new Cursor(Cursor.DEFAULT_CURSOR));
    
        // Everything "invalid" (e.g., the centerpanel above) 
        // is now re-computed. 
        this.validate();
      }
        
  • An alternative to using a Choice is to use a List, which is a scrollable list of selectable items.

In-Class Exercise 11.3 (Solution): In this exercise, you are to use a List instead of a Choice in Question 4 of the above survey. Download this template and add code to the method fourth_question() to support a List. Note that a List uses an ItemListener.


Using a ScrollPane to display large panels

Often, a panel or some other widget is too large to fit the window.

AWT provides a ScrollPane which lets you place the large widget inside, and has scrollbars to allow scanning of the whole widget.

Let us take the four questions in the above survey, place them all on one panel, and put the panel inside a ScrollPane.

Here is the desired effect:
Window

To implement these changes

  • We will remove the "Previous" and "Next" buttons from our survey.
  • We will remove the CardLayout.
  • And we will add in a ScrollPane.
Here is the code in the constructor: (source file)

    // Now the center panel. 
    centerpanel = new Panel ();
    centerpanel.setLayout (new GridLayout (4,1));
    centerpanel.add (first_question ());
    centerpanel.add (second_question());
    centerpanel.add (third_question());
    centerpanel.add (fourth_question());

    // Next, the scrollpane. 
    sc = new ScrollPane ();
    sc.add (centerpanel);

    // Add the scrollpane to the frame. 
    this.add (sc, BorderLayout.CENTER);
  

Note:

  • A ScrollPane is a special kind of container (an enhanced panel).

  • Some useful facts about ScrollPane:
    • You can set the initial "viewport".
    • You can set the "scrollbar policy", which decides whether you always display scrollbars or only when they are needed.
    • You can control the size of the scrollbar.
    • You can retrieve the current position of the scrollbar.
    • Note: a scrollpane does not receive events that are under programmer control.


Designing a form with a menubar and dialog

We will next design a simple form with a few text fields that will have the following features:

  • It will store data to a file and be able to read from the file.
  • A menubar will provide load/store and submit options.
  • A filedialog will allow the user to indicate which file.
  • An error in the filename will bring up a dialog box.
  • A popup menu will allow quick movement to the top and bottom of the form.

In-Class Exercise 11.4: First, familiarize yourself with the form by downloading the file Form.java, compiling and executing it. In particular, fill out the form, "submit" it and look at the properties file it creates in the same directory. Also observe the following features: a file dialog, a popup menu.

We will design the form in stages, starting with a simple form that writes to a properties file.


Step 1 in creating the form: writing to a file.

As a first step, we will do the following:

  • Create a Frame by extending the class Frame (as usual).

  • Create TextField's for:
    • First name.
    • Last name.
    • Net worth.


  • Create a TextArea (in which multiple lines can be entered) for the address.

  • Create two buttons, "submit" and "quit".

  • When "submit" is pressed, we will save the text entries to a file, using a Properties instance.

Here is the code: (source file)


import java.awt.*;
import java.awt.event.*;
import java.util.*;        // For "Properties". 
import java.io.*;          // For file I/O. 

class NewFrame extends Frame {

  // Data. 
  Panel centerpanel;       // For the questions. 
  ScrollPane sc;           // For placing the centerpanel. 

  TextField lastname_tf;   // To get the lastname. 
  String lastname;

  TextField firstname_tf;  // Firstname. 
  String firstname;

  TextField networth_tf;   // Net worth 
  String networth;

  TextArea address_ta;     // Address (TextArea) 
  String address;
  

  // Constructor. 
  public NewFrame (int width, int height)
  {
    this.setTitle ("Snoot Club Membership Form");
    this.setResizable (true);
    this.setBackground (Color.white);
    this.setSize (width, height);
    // this.setLayout (new BorderLayout()); 

    // First, a welcome message: 
    Label L = new Label ("Please fill out the following form:");
    L.setFont (new Font ("Serif", Font.BOLD | Font.ITALIC, 15));
    L.setForeground (Color.blue);
    this.add (L, BorderLayout.NORTH);

    // Now the center panel. 
    centerpanel = new Panel ();
    centerpanel.setLayout (new GridLayout (4,1));

    // Each of the methods first_name() etc  
    // will return a panel. 
    centerpanel.add (first_name ());
    centerpanel.add (last_name ());
    centerpanel.add (net_worth ());
    centerpanel.add (address ());

    // Next, a scrollpane for the form. 
    sc = new ScrollPane ();
    sc.add (centerpanel);
    this.add (sc, BorderLayout.CENTER);

    // Next, a panel of two buttons at the bottom. 
    Panel bottom_panel = new Panel ();

    // A submit button. 
    Button submit = new Button ("Submit");
    submit.setBackground (Color.green);
    submit.setFont (new Font ("Serif", Font.PLAIN | Font.BOLD, 15));
    submit.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  // Perform submit task. 
	  submit ();
	}  
      }
    );
    bottom_panel.add (submit);

    Button quitb = new Button ("Quit");
    quitb.setBackground (Color.red);
    quitb.setFont (new Font ("Serif", Font.PLAIN | Font.BOLD, 15));
    quitb.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  System.exit (0);
	}  
      }
    );
    bottom_panel.add (quitb);


    // Now add the panel to the frame. 
    this.add (bottom_panel, BorderLayout.SOUTH);

    // Finally, show the frame. 
    this.setVisible (true);
  }


  // Read in the first name. 

  Panel first_name ()
  {
    Panel subpanel = new Panel ();

    Label L = new Label ("First Name");
    L.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L);

    firstname_tf = new TextField (20);
    firstname_tf.setForeground (Color.blue);
    firstname_tf.addTextListener (
      new TextListener () {
        public void textValueChanged (TextEvent t)
	{
	  // Store in the string "firstname". 
	  firstname = firstname_tf.getText();
	}  
      }
    );
    subpanel.add (firstname_tf);

    return subpanel;
  }


  // Get last name. 

  Panel last_name ()
  {
    Panel subpanel = new Panel ();

    Label L = new Label ("Last Name");
    L.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L);

    lastname_tf = new TextField (20);
    lastname_tf.setForeground (Color.blue);
    lastname_tf.addTextListener (
      new TextListener () {
        public void textValueChanged (TextEvent t)
	{
	  lastname = lastname_tf.getText();
	}  
      }
    );
    subpanel.add (lastname_tf);

    return subpanel;
  }


  // Get net worth. 

  Panel net_worth ()
  {
    Panel subpanel = new Panel ();

    Label L = new Label ("Net worth (in millions)");
    L.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L);

    networth_tf = new TextField (10);
    networth_tf.setForeground (Color.blue);
    networth_tf.addTextListener (
      new TextListener () {
        public void textValueChanged (TextEvent t)
	{
	  networth = networth_tf.getText();
	}  
      }
    );
    subpanel.add (networth_tf);

    return subpanel;
  }


  // Get address via a TextArea. 

  Panel address ()
  {
    Panel subpanel = new Panel ();

    Label L = new Label ("Address");
    L.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L);

    // Default size: 4 lines, 30 chars wide. 
    address_ta = new TextArea (4, 30);
    address_ta.setForeground (Color.blue);
    address_ta.addTextListener (
      new TextListener () {
        public void textValueChanged (TextEvent t)
	{
	  address = address_ta.getText();
	}  
      }
    );
    subpanel.add (address_ta);

    return subpanel;
  }


  // Process data when ready. 

  void submit ()
  {
    // Create a file by concatenating firstname and lastname. 
    String filename = firstname + lastname;
    if (filename.length() == 0) return;

    // We will use a "Properties" instance to store data. 
    Properties p = new Properties ();
    p.put ("firstname", firstname);
    p.put ("lastname", lastname);
    p.put ("networth", networth);
    p.put ("address", address);

    // Use the "save" feature of the Properties instance. 
    try {
      FileOutputStream f = new FileOutputStream (filename);
      p.save (f, "Member information");
    }
    catch (IOException e) {
      System.out.println (e);
      System.exit (0);
    }
  }

}


public class Form1 {
  public static void main (String[] argv)
  {
    NewFrame nf = new NewFrame (500, 300);
  }
}
 

Note:

  • Each of the four entries was created in a separate method. (e.g., first_name).

  • An entry consisted of a Label and a TextField or TextArea (for the address).
    This combination was placed in a panel.

  • A ScrollPane was used for the centerpanel.

  • For a TextField or TextArea:
    • A TextListener is added.
    • The method textValueChanged() needs to be implemented in a TextListener.
    • The actual text is obtained using the getText() method:
      
          firstname_tf.addTextListener (
            new TextListener () {
              public void textValueChanged (TextEvent t)
      	{
      	  // Store in the string "firstname". 
      	  firstname = firstname_tf.getText();
      	}  
            }
          );
            
  • A Properties instance is used to store the data:
    
        Properties p = new Properties ();
        p.put ("firstname", firstname);
        p.put ("lastname", lastname);
        p.put ("networth", networth);
        p.put ("address", address);
        
  • The save() method of Properties is used to write the data to a (text) file:
    
          FileOutputStream f = new FileOutputStream (filename);
          p.save (f, "Member information");
        
  • The filename was created by concatenating the first and last names.


Step 2 in creating the form: placing features in a menu

Next, we will embellish the form in the following ways:

  • All the functions will be placed in a menu.

  • Apart from "Quit" and "Submit", we will add the following functions:
    • "Clear": to clear the entries.
    • "Load": to load entries from an existing file.


  • We will place the "Quit" and "Load" features in a "File" menu.

  • We will place "Clear" and "Submit" in an "Action" menu.

  • Each menu will be placed in the menubar.

Rather than display the whole source file, we will show pieces of it:


class NewFrame extends Frame {

  // ... Data ... 

  MenuBar mb;              // The menubar. 


  // Constructor. 
  public NewFrame (int width, int height)
  {
    // ... 

    // The menubar 
    mb = new MenuBar ();

    // Add a "File" menu with two items. 
    Menu file_menu = new Menu ("File");
    file_menu.add ("Load");
    file_menu.add ("Quit");
    file_menu.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  String item = a.getActionCommand ();
	  if (item.equals ("Load"))
	    load ();
	  else if (item.equals ("Quit"))
	    System.exit (0);
	}  
      }
    );
    mb.add (file_menu);

    // Add an "Action" menu with two items. 
    Menu action_menu = new Menu ("Action");
    action_menu.add ("Submit");
    action_menu.add ("Clear");
    action_menu.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  String item = a.getActionCommand ();
	  if (item.equals ("Submit"))
	    submit ();
	  else if (item.equals ("Clear")) {
	    firstname = "";  firstname_tf.setText (firstname);
	    lastname = "";  lastname_tf.setText (lastname);
	    networth = "";  networth_tf.setText (networth);
	    address = "";  address_ta.setText (address);
	  }
	}  
      }
    );
    mb.add (action_menu);

    // Note how a menubar is added to a Frame. 
    this.setMenuBar (mb);

    // Finally, show the frame. 
    this.setVisible (true);
  }

  // ... first_name() and other methods ... 


  // Loading from a file. 

  Properties info;

  void load () 
  {
    // Create the filename. 
    String filename = firstname + lastname;

    info = new Properties ();
    try {
      FileInputStream f = new FileInputStream (filename);
      info.load (f);
      // First and lastname are already entered. 
      networth = info.getProperty ("networth");
      networth_tf.setText (networth);
      address = info.getProperty ("address");
      address_ta.setText (address);
    }
    catch (IOException e) {
      System.out.println ("No such file ... fatal error");
      System.exit (0);
    }
  }

  

Note:

  • The steps in creating a menubar are:
    • Create a MenuBar:
      
          mb = new MenuBar ();
            
    • Create Menu's and add them to the MenuBar:
      
          // Add a "File" menu with two items. 
          Menu file_menu = new Menu ("File");
          file_menu.add ("Load");
          file_menu.add ("Quit");
          file_menu.addActionListener (
            new ActionListener () {
              public void actionPerformed (ActionEvent a)
      	{
      	  String item = a.getActionCommand ();
      	  if (item.equals ("Load"))
      	    load ();
      	  else if (item.equals ("Quit"))
      	    System.exit (0);
      	}  
            }
          );
          mb.add (file_menu);
            
    • Note: an individual menu requires an ActionListener.
    • The MenuBar itself is added to the frame using a special method in Frame:
      
          // Note how a menubar is added to a Frame. 
          this.setMenuBar (mb);
            
  • The text in the text components can be set using the setText() method:
    
    	  else if (item.equals ("Clear")) {
    	    firstname = "";  firstname_tf.setText (firstname);
    	    lastname = "";  lastname_tf.setText (lastname);
    	    networth = "";  networth_tf.setText (networth);
    	    address = "";  address_ta.setText (address);
        
  • The load() method of Properties is used to load data from a file:
    
          FileInputStream f = new FileInputStream (filename);
          info.load (f);
          // First and lastname are already entered. 
          networth = info.getProperty ("networth");
          networth_tf.setText (networth);
          address = info.getProperty ("address");
          address_ta.setText (address);
        
    (The assumption is that the firstname and lastname have been entered).

In-Class Exercise 11.5 (Solution): In this exercise, you will use the following template to add a "Help" menu to the menubar:

  • Your help menu will have two items: "About" and "Help...".
  • When activated, the "About" menu item should call the method about() in the frame.
  • Similarly, the method help() should be called whenever the user goes to "Help..." in the menu.
  • It is advisable but not necessary to use the MenuBar method setHelpMenu() instead of add() when adding the help menu.
  • For now, do not implement anything in the about() method. You will deal with this in Ex.11.6 below.
  • In the method help(), display a new top-level frame with a quit button and any rude help message you like, such as "Sorry, you're on your own".


Step 3 in creating the form: a file dialog.

Next, we will allow the user to name a file using a file dialog:

  • We will add a "LoadNew" menuitem for this purpose.
  • We will also modify the "load" part of the code to use a File instance:
    • Since the user may decide to load from a different directory, we need to construct the full pathname.
    • A File is useful because you can get a lot of directory information from it.

Again, we will only show parts of the whole source file:


class NewFrame extends Frame {

   // ... 

  // Constructor. 
  public NewFrame (int width, int height)
  {

    // ... 

    // Add a "File" menu with two items. 
    Menu file_menu = new Menu ("File");
    file_menu.add ("Load");
    file_menu.add ("LoadFromFile");
    file_menu.add ("Quit");
    file_menu.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  String item = a.getActionCommand ();
	  if (item.equals ("Load")) 
	    load ();
	  if (item.equals ("LoadFromFile"))
	    load_new ();
	  else if (item.equals ("Quit"))
	    System.exit (0);
	}  
      }
    );
    mb.add (file_menu);

    // ... 

  }

  // ... other methods ... 

  // Load information from an existing Properties file. 

  Properties info;

  // We will use a File instance. 

  void load ()
  {
    String dir = null;
    String filename = firstname + lastname;
    File file = new File (dir, filename);
    load (file);
  }

  void load (File file) 
  {
    info = new Properties ();
    try {
      FileInputStream f = new FileInputStream (file);
      info.load (f);
      firstname = info.getProperty ("firstname");
      firstname_tf.setText (firstname);
      lastname = info.getProperty ("lastname");
      lastname_tf.setText (lastname);
      networth = info.getProperty ("networth");
      networth_tf.setText (networth);
      address = info.getProperty ("address");
      address_ta.setText (address);
    }
    catch (IOException e) {
      System.out.println ("No such file ... fatal error");
      System.exit (0);
    }
    
  }


  // Use a file dialog. 

  void load_new ()
  {
    // Create the FileDialog instance. 
    FileDialog fd = 
      new FileDialog (this, "File to load from", FileDialog.LOAD);

    // Note: need to make it visible. 
    fd.setVisible (true);

    // When the user is done, get the info. 
    String dir = fd.getDirectory ();
    String filename = fd.getFile ();
    String fullname = dir + filename;

    // Create a full filename. 
    File file = new File (fullname);
    load (file);
  }

  

Note:

  • We added the "LoadNew" menuitem to the "File" menu:
    
        file_menu.add ("LoadFromFile");
        
  • Accordingly, the ActionListener for the menu needs to handle this new item:
    
    	  if (item.equals ("LoadFromFile"))
    	    load_new ();
        
  • The methods first_name() etc remain the same.

  • A new method, load (File file) was added.
    The only difference is that a File instance can be passed to the constructor of FileInputStream:
    
          FileInputStream f = new FileInputStream (file);
        
  • Note that a null directory indicates the current directory:
    
      void load ()
      {
        String dir = null;
        String filename = firstname + lastname;
        File file = new File (dir, filename);
        load (file);
      }
        
  • A FileDialog requires the Frame reference (via this) and either FileDialog.LOAD (read) or FileDialog.SAVE (write):
    
        FileDialog fd = 
          new FileDialog (this, "File to load from", FileDialog.LOAD);
    
        // Note: need to make it visible. 
        fd.setVisible (true);
        
    Note: the FileDialog is a top-level window and so, needs to be made visible.

  • The FileDialog grabs focus and therefore, upon closing, execution continues after it is made visible:
    
        fd.setVisible (true);
    
        // When the user is done, get the info. 
        String dir = fd.getDirectory ();
        String filename = fd.getFile ();
        String fullname = dir + filename;
        


Step 4 in creating the form: an error dialog.

Thus far, we have not handled errors gracefully.

For example, if the filename was incorrect, we killed the program:


    try {
      FileInputStream f = new FileInputStream (filename);

      // ... 

    }
    catch (IOException e) {
      System.out.println ("No such file ... fatal error");
      System.exit (0);
    }
  

Next, we will bring up a simple dialog box with an "OK" button to indicate the error.

This code is added to the load() method: (source file)


  Dialog error_dialog;

  void load (File file) 
  {
    info = new Properties ();
    try {
      FileInputStream f = new FileInputStream (file);
      info.load (f);
      firstname = info.getProperty ("firstname");
      firstname_tf.setText (firstname);
      lastname = info.getProperty ("lastname");
      lastname_tf.setText (lastname);
      networth = info.getProperty ("networth");
      networth_tf.setText (networth);
      address = info.getProperty ("address");
      address_ta.setText (address);
    }
    catch (IOException e) {
      // Handle a file error more gracefully. 
      error_dialog = new Dialog (this, true);
      error_dialog.setSize (200, 100);
      error_dialog.setBackground (Color.yellow);

      Label L = new Label ("File does not exist");
      Button ok = new Button ("OK");
      ok.addActionListener (
        new ActionListener () {
          public void actionPerformed (ActionEvent a)
	  {
	    error_dialog.dispose();
  	  }  
        }
      );

      error_dialog.setLayout (new BorderLayout());
      error_dialog.add (L, BorderLayout.CENTER);
      error_dialog.add (ok, BorderLayout.SOUTH);

      // Need to make it visible. 
      error_dialog.setVisible (true);
    }
  

Note:

  • A Dialog instance above is passed two parameters:
    
          error_dialog = new Dialog (this, true);
        
    • The Frame reference (via this).
    • The mode (whether it grabs focus or not):
      • A modal dialog will not allow anything else to happen with respect to the frame until the dialog is complete.
      • A modeless dialog allows concurrency.
      • Note: it is common to use a modal dialog for errors.


  • The right way to kill a dialog is to call its dispose() method:
    
          Button ok = new Button ("OK");
          ok.addActionListener (
            new ActionListener () {
              public void actionPerformed (ActionEvent a)
    	  {
    	    error_dialog.dispose();
      	  }  
            }
          );
         
  • Finally, since a dialog is a top-level window, it needs to be made visible:
    
          error_dialog.setVisible (true);
       

In-Class Exercise 11.6 (Solution): In this exercise, use your code from Ex.11.5 or, if that was not completed, the following template to add a dialog for the "About" option in the "Help" menu. Typically, "About" displays copyright information.


Step 5 in creating the form: a popup menu

Finally, we will add a simple popup menu with two items:

  • "top": to scroll to the top.
  • "bottom": to scroll to the bottom.
(Along the way, we will learn how to set the scroll position).

Here are the relevant parts of the source file:


class NewFrame extends Frame {

  // ... 

  PopupMenu pm;            // The popup menu. 


  // Constructor. 
  public NewFrame (int width, int height)
  {
    // ... 

    // A pop-up menu 
    pm = new PopupMenu ();
    pm.add ("Top");
    pm.add ("Bottom");
    pm.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  String item = a.getActionCommand ();
	  if (item.equals ("Top"))
	    top ();
	  else if (item.equals ("Bottom"))
	    bottom ();
	}  
      }
    );
    // We add this to the frame. 
    this.add (pm);    

    // ... 

  }


  // Methods like first_name() method are slightly modified: 

  Panel first_name ()
  {
    Panel subpanel = new Panel ();

    Label L = new Label ("First Name");
    L.setFont (new Font ("SansSerif", Font.ITALIC, 20));
    subpanel.add (L);

    // Listen for a Popup click. 
    L.addMouseListener (getMouseListener (L));

    firstname_tf = new TextField (20);
    firstname_tf.setForeground (Color.blue);
    firstname_tf.addTextListener (
      new TextListener () {
        public void textValueChanged (TextEvent t)
	{
	  firstname = firstname_tf.getText();
	}  
      }
    );
    subpanel.add (firstname_tf);
    subpanel.addMouseListener (getMouseListener(subpanel));

    return subpanel;
  }


  // ... 


  // Create a Listener for popup's. 

  MouseListener getMouseListener (Component c)
  {
    class PopupMouseListener extends MouseAdapter {
      Component c;
      public PopupMouseListener (Component c)
      {
	this.c = c;
      }
      public void mousePressed (MouseEvent m)
      {
	// We need this special check. 
	if (m.isPopupTrigger ()) 
	  pm.show (c, m.getX(), m.getY());
	// Note: this "show" method is not deprecated. 
      }  
    }

    return new PopupMouseListener (c);
  }


  // Handle the "go to top" event. 

  void top ()
  {
    sc.setScrollPosition (0,0);
  }

  // Handle the "go to bottom" event. 

  void bottom ()
  {
    int height = sc.getViewportSize().height;
    sc.setScrollPosition (0,height);
  }

}
  

Note:

  • A PopupMenu uses an ActionListener:
    
        pm = new PopupMenu ();
        pm.add ("Top");
        pm.add ("Bottom");
        pm.addActionListener (
          new ActionListener () {
            public void actionPerformed (ActionEvent a)
    	{
    	  String item = a.getActionCommand ();
    	  if (item.equals ("Top"))
    	    top ();
    	  else if (item.equals ("Bottom"))
    	    bottom ();
    	}  
          }
        );
        
  • We need to set up a MouseListener wherever we want the popup menu to appear:
    • In our case, that's each Label in the form.
    • Thus, the "first name" Label is given a MouseListener:
      
        Panel first_name ()
        {
          // ... 
      
          Label L = new Label ("First Name");
          L.setFont (new Font ("SansSerif", Font.ITALIC, 20));
          subpanel.add (L);
      
          // Listen for a Popup click. 
          L.addMouseListener (getMouseListener (L));
      
          // ... 
        }
             
    • Since the MouseListener is similar for all the labels, we have written a method to create one:
      
        MouseListener getMouseListener (Component c)
        {
          class PopupMouseListener extends MouseAdapter {
            Component c;
            public PopupMouseListener (Component c)
            {
      	this.c = c;
            }
            public void mousePressed (MouseEvent m)
            {
      	// We need this special check. 
      	if (m.isPopupTrigger ()) 
      	  pm.show (c, m.getX(), m.getY());
      	// Note: this "show" method is not deprecated. 
            }  
          }
      
          return new PopupMouseListener (c);
        }
           
    • Note that a special method isPopupTrigger() is used to detect whether the mouse-event is a genuine popup (e.g, a right-click).
    • Only if this is the case do we display the popup menu.


  • The popup menu itself is displayed via a special method in PopupMenu called show():
    
    	  pm.show (c, m.getX(), m.getY());
        
    This method takes as parameters the component (e.g., label) in which the menu pops up and the location.

  • The scroll position can be adjusted using the setScrollPosition() method in ScrollPane.

  • The method getViewportSize() in ScrollPane returns the height of the complete scrollpane as a Dimension instance.


Scrollbars

In this section, we will learn how to create individual scrollbars using the time-honored example of setting a red-green-blue combination with scrollbars:

  • One scrollbar for each of red, blue and green.
  • The setting on the scrollbar will be translated to a value between 0 and 255.
  • The RGB (red-blue-green) combination will be used to create a color, which will be displayed on a canvas.
For example:
Window

Here is the code: (source file)


import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;

class NewFrame extends Frame {

  // Data. 

  Scrollbar             // One scrollbar for each color. 
    red_bar,
    blue_bar,
    green_bar;

  int                   // Intensity value for each color. 
    red_value = 0,
    blue_value = 0,
    green_value = 0;

  Canvas c;             // To display the mixed color. 


  // Constructor. 

  public NewFrame (int width, int height)
  {
    this.setTitle ("RGB combination");
    this.setResizable (true);
    this.setBackground (Color.cyan);
    this.setSize (width, height);
    // this.setLayout (new BorderLayout()); 

    // Three scrollbars and the canvas will 
    // be added to a panel. 
    Panel p = new Panel ();
    p.setLayout (new GridLayout (4,1));

    // The canvas on which to display the result. 
    c = new Canvas ();
    c.setBackground (Color.white);
    p.add (c);

    // A local Listener for the scrollbars. 
      class ColorAdjustmentListener implements AdjustmentListener {
        public void adjustmentValueChanged (AdjustmentEvent a)
	{
	  adjust ();
	}
      }

    red_bar = new Scrollbar (Scrollbar.HORIZONTAL, 0, 10, 0, 255);
    red_bar.addAdjustmentListener (new ColorAdjustmentListener());
    p.add (red_bar);

    green_bar = new Scrollbar (Scrollbar.HORIZONTAL, 0, 10, 0, 255);
    green_bar.addAdjustmentListener (new ColorAdjustmentListener());
    p.add (green_bar);

    blue_bar = new Scrollbar (Scrollbar.HORIZONTAL, 0, 10, 0, 255);
    blue_bar.addAdjustmentListener (new ColorAdjustmentListener());
    p.add (blue_bar);

    this.add (p, BorderLayout.CENTER);

    // A quit button for the application. 
    Button quitb = new Button ("Quit");
    quitb.setBackground (Color.red);
    quitb.setFont (new Font ("Serif", Font.PLAIN | Font.BOLD, 15));
    quitb.addActionListener (
      new ActionListener () {
        public void actionPerformed (ActionEvent a)
	{
	  System.exit (0);
	}  
      }
    );
    this.add (quitb, BorderLayout.SOUTH);


    // Finally, show the frame. 
    this.setVisible (true);
  }


  // No-parameter constructor. 
  public NewFrame () 
  {
    this (500, 300);
  }


  // Create the composite color. 

  public void adjust ()
  {
    // Get the new values. 
    red_value = red_bar.getValue();
    green_value = green_bar.getValue ();
    blue_value = blue_bar.getValue ();

    // Set the color in each scrollbar. 
    red_bar.setBackground (new Color (red_value, 0, 0));
    green_bar.setBackground (new Color (0, green_value, 0));
    blue_bar.setBackground (new Color (0, 0, blue_value));

    // Create the composite color. 
    Color new_color = new Color (red_value, green_value, blue_value); 
    c.setBackground (new_color);
    c.repaint ();
  }

}


public class TestScrollbar {
  public static void main (String[] argv)
  {
    NewFrame nf = new NewFrame (500, 300);
  }
}
  

Note:

  • The constructor for Scrollbar is given four parameters above:
    
        red_bar = new Scrollbar (Scrollbar.HORIZONTAL, 0, 10, 0, 255);
        
    • An orientation (the constant Scrollbar.VERTICAL or Scrollbar.HORIZONTAL).
    • An initial value (0, above).
    • The size of the bar (10 pixels above).
    • The low-end of the range (0, above).
    • The high-end of the range (255, above).


  • An AdjustmentListener is required to listen for scrollbar movements.

  • Above, we used a local class to create these listeners.

  • The method getValue() returns the current position of the bar in terms of the specified range.


Keyboard events

Next, we will look at an example of handling keyboard events:

We will modify the scrollbar example in the following ways:

  • We will quit the application if the letter "q" is typed.
  • We will increase the red-intensity when the Right-arrow is pressed.
  • We will decrease the red-intensity when the Left-arrow is pressed.

Here are the relevant parts of the source file:


class NewFrame extends Frame {

  // ... 

  // Constructor. 
  public NewFrame (int width, int height)
  {
    // ... 

    // A KeyListener for the canvas. 
    c.addKeyListener (getKeyListener());

    // A KeyListener for the red scrollbar. 
    red_bar.addKeyListener (getKeyListener());

    // A KeyListener for the green scrollbar. 
    green_bar.addKeyListener (getKeyListener());

    // A KeyListener for the blue scrollbar. 
    blue_bar.addKeyListener (getKeyListener());

    // ... 
  }


  // ... 


  // Create a KeyListener to be used everywhere. 

  KeyListener getKeyListener ()
  {
    return new KeyAdapter () {
      public void keyPressed (KeyEvent k)
      {
	if (k.getKeyCode() == KeyEvent.VK_Q)
  	  // "q" for quit. 
	  System.exit (0);
	else if (k.getKeyCode() == KeyEvent.VK_LEFT) {	  
	  // Left arrow. 
          red_bar.setValue (Math.max (0, red_value-10));
	  adjust ();
	}
	else if (k.getKeyCode() == KeyEvent.VK_RIGHT) {
	  // Right arrow. 
	  red_bar.setValue (Math.min (255, red_value+10));
	  adjust ();
	}
      }
    };
  }

}
  

Note:

  • We added a KeyListener to the various components: the canvas and the three scrollbars.

  • A KeyListener is defined as:
    
      interface KeyListener extends EventListener {
        public void keyPressed (KeyEvent e);
        public void keyReleased (KeyEvent e);
        public void keyTyped (KeyEvent e);
      }
        
  • We have overridden only the keyPressed() method.

  • The actual key pressed is returned via the KeyEvent parameter.

  • The getKeyCode() method of KeyEvent returns a code for the key pressed.

  • Each code is a constant in the class KeyEvent.

  • The codes usually start with "VK" (for Virtual Key).

  • Examples of codes: (the list is long)
    • KeyEvent.VK_Q - the letter "q" (or "Q").
    • KeyEvent.VK_SHIFT - shift key.
    • KeyEvent.VK_CONTROL - control key.
    • KeyEvent.VK_F3 - the F3 function key.
    • KeyEvent.VK_UP - the up arrow.

In-Class Exercise 11.7 (Solution): Download the following template and add the following functionality to the above scrollbar program: the user should be able to use the UP and DOWN arrows to cycle between the colors (scrollbars). The only code you need to add is in the keyPressed() method.