Module 11: Swing Part II - GUI Components


An overview of some Swing GUI components

 
For GUI-building, here are some "standard" components:

Each component can generate events and therefore has one or more associated event-listeners.

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

Exercise 11.1: Download Survey.java, 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 one 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 JFrame {

  // 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:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

class NewFrame extends JFrame {

    JPanel 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.setSize (width, height);

        Container cPane = this.getContentPane();
        // cPane.setLayout (new BorderLayout()); 

        // First, a welcome message, as a Label. 
        JLabel L = new JLabel ("<html><b>Are you elitist enough for our exclusive club?"
                          + " <br>Fill out the form and find out</b></html>");
        L.setForeground (Color.blue);
        cPane.add (L, BorderLayout.NORTH);

        // Now the center panel with the questions. 
        card = new CardLayout ();
        centerpanel = new JPanel ();
        centerpanel.setLayout (card);
        centerpanel.setOpaque (false);

        // Each question will be created in a separate method. 
        // The cardlayout requires a label as second parameter. 
        centerpanel.add (firstQuestion (), "1");
        centerpanel.add (secondQuestion(), "2");
        centerpanel.add (thirdQuestion(), "3");
        centerpanel.add (fourthQuestion(), "4");
        cPane.add (centerpanel, BorderLayout.CENTER);

        // Next, a panel of four buttons at the bottom. 
        // The four buttons: quit, submit, next-question, previous-question. 
        JPanel bottomPanel = getBottomPanel ();
        cPane.add (bottomPanel, BorderLayout.SOUTH);

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

    // ... 
}
  

Note:

Here is the complete source: (source file)

class NewFrame extends JFrame {

    JPanel 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.setSize (width, height);

        Container cPane = this.getContentPane();
        // cPane.setLayout (new BorderLayout()); 

        // First, a welcome message, as a Label. 
        JLabel L = new JLabel ("<html><b>Are you elitist enough for our exclusive club?"
                          + " <br>Fill out the form and find out</b></html>");
        L.setForeground (Color.blue);
        cPane.add (L, BorderLayout.NORTH);

        // Now the center panel with the questions. 
        card = new CardLayout ();
        centerpanel = new JPanel ();
        centerpanel.setLayout (card);
        centerpanel.setOpaque (false);

        // Each question will be created in a separate method. 
        // The cardlayout requires a label as second parameter. 
        centerpanel.add (firstQuestion (), "1");
        centerpanel.add (secondQuestion(), "2");
        centerpanel.add (thirdQuestion(), "3");
        centerpanel.add (fourthQuestion(), "4");
        cPane.add (centerpanel, BorderLayout.CENTER);

        // Next, a panel of four buttons at the bottom. 
        // The four buttons: quit, submit, next-question, previous-question. 
        JPanel bottomPanel = getBottomPanel ();
        cPane.add (bottomPanel, BorderLayout.SOUTH);

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

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


    JPanel getBottomPanel ()
    {
        // Create a panel into which we will place buttons. 
        JPanel bottomPanel = new JPanel ();

        // A "previous-question" button. 
        JButton backward = new JButton ("Previous question");
        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);
	        }  
            }
        );
        bottomPanel.add (backward);

        // A forward button. 
        JButton forward = new JButton ("Next question");
        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);
	        }  
            }
        );
        bottomPanel.add (forward);

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

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

        return bottomPanel;
    }


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

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

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

        return subpanel;
    }


    // Second question. 

    JPanel secondQuestion ()
    {
        JPanel subpanel = new JPanel ();

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

        return subpanel;
    }


    // Third question. 

    JPanel thirdQuestion ()
    {
        JPanel subpanel = new JPanel ();

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

        return subpanel;
    }


    // Fourth question. 

    JPanel fourthQuestion ()
    {
        JPanel subpanel = new JPanel ();

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

        return subpanel;
    }


    // This method is called after submit is pressed. 

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

}


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

Note:

When executed, the result looks like this:
Window
 

Exercise 11.2: Add a 5th question to the survey.
 


Using a JTextField 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 JTextField is best.

Here is the code for Question1: (source file)

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

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

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

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

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

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

    JLabel L5 = new JLabel ("    3. French Riviera");
    L5.setFont (new Font ("SansSerif", Font.ITALIC, 15));
    subpanel.add (L5);

    JLabel L6 = new JLabel ("    4. Musha Cay");
    L6.setFont (new Font ("SansSerif", Font.ITALIC, 15));
    subpanel.add (L6);

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

    // Here's the textfield to get user-input. 
    tf = new JTextField ();
    tf.addActionListener (
        new ActionListener () {
            // This interface has only one method. 
            public void actionPerformed (ActionEvent a)
   	    {
	        String q1String = a.getActionCommand();
   	        if (q1String.equals ("2"))
	            q1Score = 2;
   	        else if (q1String.equals ("3"))
    	            q1Score = 3;
	        else if (q1String.equals ("4"))
	            q1Score = 4;
	        else
	            q1Score = 1;
	    }     
        }
    );
    subpanel.add (tf);

    return subpanel;
  

When executed, Question 1 looks like this:
Window

Note:

  • We used a JLabel for each line of output text, e.g.,
        JLabel L5 = new JLabel ("    3. French Riviera");
        L5.setFont (new Font ("SansSerif", Font.ITALIC, 20));
        subpanel.add (L5);
        
  • A JTextField also uses an ActionListener.
  • A score is assigned (for our particular application) based on the input.

  • Some useful facts about JTextField's:
    • You can set the length of a TextField (using the setColumns() method or the constructor).
    • You can control display and editing features using methods in JTextComponent which JTextField extends.
 


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 JCheckbox's is convenient:
Window

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. 

  JPanel secondQuestion ()
  {
      JPanel subpanel = new JPanel ();
      subpanel.setLayout (new GridLayout (7,1));

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

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

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

      // Initialize the selections to false. 
      q2Option1 = q2Option2 = q2Option3 = q2Option4 = false;

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

      // Second checkbox etc ... (similar) ... 
  }
  

Note:

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

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

  • The JCheckbox uses an ItemListener.

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

  • The isSelected() method returns true if the box is "clicked on".

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


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

 

Suppose we want to allow only one selection to be made.

This can be achieved by associating a ButtonGroup instance with a group of JRadioButton's.

For example, in Question 3: (source file)

  // The third question allows only one among four choices 
  // to be selected. We will use radio buttons. 

  JPanel thirdQuestion ()
  {
      JPanel subpanel = new JPanel ();
      subpanel.setLayout (new GridLayout (6,1));

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

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

      // First, create the ButtonGroup instance. 
      // We will add radio buttons to this group. 
      ButtonGroup bGroup = new ButtonGroup();

      // First radiobutton. 
      JRadioButton r1 = new JRadioButton ("Safeway brand");
      r1.addItemListener (
          new ItemListener () {
              public void itemStateChanged (ItemEvent i)
	      {
	          JRadioButton r = (JRadioButton) i.getSource();
                  if (r.isSelected()) q3Score = 1;
	      }
          }
      );
      bGroup.add (r1);
      subpanel.add (r1);

    // Second radiobutton etc ... (similar) 
  }
  

Note:

  • First, an instance of ButtonGroup is created:
        ButtonGroup bGroup = new ButtonGroup();
        
  • We then add radio buttons to this instance.
        JRadioButton r1 = new JRadioButton ("Who cares?");
        bGroup.add (r1);
        

Here is the result (the radio button looks different - a circle):
Window
 


Using a JList (Question 4)

 

For Question 4, we want a list:
Window

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

    JPanel subpanel = new JPanel ();
    subpanel.setLayout (new GridLayout (3,1));

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

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

    // Create a JList with options. 
    String[] movies = { "Fast & Furious 19", "Star Wars", "Citizen Kane", 
                        "Le film d'art avec sous-titres"};
    q4Choice = new JList (movies);
    q4Score = 1;
    q4Choice.addListSelectionListener (
        new ListSelectionListener () {
            public void valueChanged (ListSelectionEvent e)
	    {
	        q4Score = 1 + q4Choice.getSelectedIndex();
	    }
        }
    );
    subpanel.add (q4Choice);

    return subpanel;
  

Note:

  • The constructor of JList is used to add items.

  • The listener used is ListSelectionListener, which requires implementation of the valueChanged() method.

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

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

  • The calculation of a score is done in the method computeResults() (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 computeResults):
      void computeResult ()
      {
          // Clear the center panel. 
          centerpanel.removeAll();
    
          // Create a new panel to display in the center. 
          JPanel subpanel = new JPanel (new GridLayout (5,1));
    
          // ... Compute scores 
    
          // Now add the new subpanel. 
          centerpanel.add (subpanel, "5");
    
          // Need to mark the centerpanel as "altered" 
          centerpanel.invalidate();
    
          // Everything "invalid" (e.g., the centerpanel above) 
          // is now re-computed. 
          this.validate();
      }
        
  • An alternative to using a JList is to use a JComboBox.
 

Exercise 11.3: In this exercise, you are to use a JComboBox instead of a JList in Question 4 of the above survey. Download this template and add code to the method fourthQuestion() to support a JComboBox. Note that a JComboBox uses an ActionListener.
 


Using a JScrollPane to display large panels

 

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

Swing provides a JScrollPane 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 JScrollPane.

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)
    // The center panel with the questions. 
    centerpanel = new JPanel ();

    // Use GridLayout for vertical layout of questions. 
    centerpanel.setLayout (new GridLayout (4,1));

    // Each question will be created in a separate method. 
    // The cardlayout requires a label as second parameter. 
    centerpanel.add (firstQuestion (), "1");
    centerpanel.add (secondQuestion(), "2");
    centerpanel.add (thirdQuestion(), "3");
    centerpanel.add (fourthQuestion(), "4");

    // Add the center panel to the scroll pane and put it in the frame. 
    // A scrollpane for the center. 
    JScrollPane scrollpane = new JScrollPane (centerpanel);
    cPane.add (scrollpane, BorderLayout.CENTER);

    // ... 
  

Note:

  • We used a GridLayout to lay the subpanels vertically.
    Not using it causes horizontal layout:
    Window

  • A JScrollPane is a special kind of container.

  • Some useful facts about JScrollPane:
    • 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.
 


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.
 

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: building the widgets

 

As a first step, we will do the following:

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

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


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

  • Create a menu bar with a "File" and "Action" menu.

  • When the "File" menu is dropped: show menu items "Load From File" and "Quit".

  • When the "Action" menu is dropped: show menu items "Clear" and "Submit".

  • Right now, we will not save to a file, but just print the values read from the widgets (to System.out).

Here is the code: (source file)

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;

class NewFrame extends JFrame {

    // Data. 
    JPanel centerpanel;         // For the questions. 
    JScrollPane sc;             // For placing the centerpanel. 

    JTextField lastnameTextF;   // To get the lastname. 
    String lastname;

    JTextField firstnameTextF;  // Firstname. 
    String firstname;

    JTextField networthTextF;   // Net worth 
    String networth;

    JTextArea addressTextA;     // Address (TextArea) 
    String address;

    JMenuBar mb;                // The menubar. 


    // Constructor. 
    public NewFrame (int width, int height)
    {
        this.setTitle ("Snoot Club Membership Form");
        this.setResizable (true);
        this.setSize (width, height);

        Container cPane = this.getContentPane();
        // cPane.setLayout (new BorderLayout()); 

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

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

        // Each widget is created in a separate method. 
        centerpanel.add ( firstName() );
        centerpanel.add ( lastName() );
        centerpanel.add ( netWorth() );
        centerpanel.add ( address () );

        // Next, the scrollpane to contain the main panel. 
        sc = new JScrollPane (centerpanel);
        cPane.add (sc, BorderLayout.CENTER);

        // Create a menubar and add two menus. 
        mb = new JMenuBar ();
        mb.add ( makeFileMenu() );
        mb.add ( makeActionMenu() );

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

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


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


    // Read in the first name. 

    JPanel firstName ()
    {
        JPanel subpanel = new JPanel ();

        // First, a label before the textfield. 
        JLabel L = new JLabel ("First Name");
        L.setFont (new Font ("SansSerif", Font.ITALIC, 20));
        subpanel.add (L);

        // Create and add the textfield. 
        firstnameTextF = new JTextField (20);
        firstnameTextF.setForeground (Color.blue);
        subpanel.add (firstnameTextF);

        return subpanel;
    }


    // Get last name. 
 
    JPanel lastName ()
    {
        JPanel subpanel = new JPanel ();

        // The "last name" label. 
        JLabel L = new JLabel ("Last Name");
        L.setFont (new Font ("SansSerif", Font.ITALIC, 20));
        subpanel.add (L);

        lastnameTextF = new JTextField (20);
        lastnameTextF.setForeground (Color.blue);
        subpanel.add (lastnameTextF);

        return subpanel;
    }


    // Get net worth. 

    JPanel netWorth ()
    {
        JPanel subpanel = new JPanel ();

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

        networthTextF = new JTextField (10);
        networthTextF.setForeground (Color.blue);
        subpanel.add (networthTextF);

        return subpanel;
    }


    // Get address via a TextArea. 

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

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

        addressTextA = new JTextArea (4, 30);
        addressTextA.setForeground (Color.blue);
        subpanel.add (addressTextA);

        return subpanel;
    }


    // Create the File menu and associated listeners. 

    JMenu makeFileMenu ()
    {
        // Add a "File" menu with two items. 
        JMenu fileMenu = new JMenu ("File");

        // "Load From File" menu item 
        JMenuItem loadFromFileMenuItem = new JMenuItem ("Load From File");
        loadFromFileMenuItem.addActionListener (
            new ActionListener () {
                public void actionPerformed (ActionEvent a)
	        {
                    loadFromFile ();
	        }  
           }
        );
        fileMenu.add (loadFromFileMenuItem);

        // "Quit" menu item 
        JMenuItem quitMenuItem = new JMenuItem ("Quit");
        quitMenuItem.addActionListener (
            new ActionListener () {
                public void actionPerformed (ActionEvent a)
	        {
	            System.exit (0);
	        }  
            }
        );
        fileMenu.add (quitMenuItem);

        return fileMenu;
    }
  


    // Create the Action Menu and its listeners. 

    JMenu makeActionMenu ()
    {
        // Add an "Action" menu with two items. 
        JMenu actionMenu = new JMenu ("Action");

        // "Submit" menu item 
        JMenuItem submitMenuItem = new JMenuItem ("Submit");
        submitMenuItem.addActionListener (
            new ActionListener () {
                public void actionPerformed (ActionEvent a)
	        {
                    submit ();
	        }  
            }
        );
        actionMenu.add (submitMenuItem);

        // "Clear" menu item 
        JMenuItem clearMenuItem = new JMenuItem ("Clear");
        clearMenuItem.addActionListener (
            new ActionListener () {
                public void actionPerformed (ActionEvent a)
	        {
                    // Clear all the fields. 
                    firstname = "";  firstnameTextF.setText (firstname);
                    lastname = "";  lastnameTextF.setText (lastname);
                    networth = "";  networthTextF.setText (networth);
                    address = "";  addressTextA.setText (address);
	        }  
            }
        );
        actionMenu.add (clearMenuItem);

        return actionMenu;
    }


    // Process data when ready. 

    void submit ()
    {
        System.out.println ("Submit");
    }



    // Use a file dialog. 

    void loadFromFile ()
    {
        System.out.println ("Load From File");
    }

}


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

Note:

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

  • An entry consisted of a JLabel and a JTextField or JTextArea (for the address).
    This combination was placed in a panel.

  • A JScrollPane was used for the centerpanel.

  • To set (or clear) the text out of a JTextField or JTextArea, call the setText() method.
              firstname = "";  firstnameTextF.setText (firstname);
              lastname = "";  lastnameTextF.setText (lastname);
              networth = "";  networthTextF.setText (networth);
              address = "";  addressTextA.setText (address);
          
  • The menubar is created with a JMenuBar instance, and is "added" using a special method:
        this.setJMenuBar (mb);
      
  • To a menubar, we add JMenuItem's.

  • Each JMenuItem has an associated ActionListener, e.g.,
        // "Quit" menu item 
        JMenuItem quitMenuItem = new JMenuItem ("Quit");
        quitMenuItem.addActionListener (
            new ActionListener () {
                public void actionPerformed (ActionEvent a)
    	    {
    	        System.exit (0);
    	    }  
            }
        );
        fileMenu.add (quitMenuItem);
       
 


Step 2 in creating the form: working with files.

 

Next, we will use a Properties instance to store the data:

  • We will store the data to a filename constructed by concatenating the "firstname" and "lastname".

    We will place the data in a Properties instance.

Here is the code: (source file)

  Properties info;

  void submit ()
  {
      // Retrieve data from the widgets. 
      lastname = lastnameTextF.getText();
      firstname = firstnameTextF.getText();
      networth = networthTextF.getText();
      address = addressTextA.getText();

      // 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.store (f, "Member information");
      }
      catch (IOException e) {
          System.out.println (e);
          System.exit (0);
      }
  }
  

Note:

  • A java.util.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 store() method of Properties is used to write the data to a (text) file:
          FileOutputStream f = new FileOutputStream (filename);
          p.store (f, "Member information");
        
  • The filename was created by concatenating the first and last names.
 

Exercise 11.5: 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.
  • 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".
Once you've completed the above, see what happens when you use the setHelpMenu() method of JMenuBar instead of add() when adding the help menu.
 


Step 3 in creating the form: a file dialog.

 

Next, we will fill out the loadFromFile method:

  • We will use a JFileChooser to prompt the user for a file name.

Here is the code in loadFromFile(): (source file)

  void loadFromFile ()
  {
      // Create the FileDialog instance. 
      JFileChooser fc =  new JFileChooser();
      int returnCode = fc.showOpenDialog (null);
    
      File file = fc.getSelectedFile();
    
      info = new Properties ();
      try {
          // Load the properties file. 
          FileInputStream f = new FileInputStream (file);
          info.load (f);

          // Extract the info. 
          firstname = info.getProperty ("firstname");
          firstnameTextF.setText (firstname);
          lastname = info.getProperty ("lastname");
          lastnameTextF.setText (lastname);
          networth = info.getProperty ("networth");
          networthTextF.setText (networth);
          address = info.getProperty ("address");
          addressTextA.setText (address);
      }
      catch (IOException e) {
          System.out.println ("Couldn't load file");
          System.exit (0);
      }
    
  }
  

Note:

  • A JFileChooser can be brought up to "open" or "save" a file.

  • To "open", use
        int returnCode = fc.showOpenDialog (null);
       
  • The return code indicates whether a file was selected.

  • The load() method of Properties is used to load data from a file:
          FileInputStream f = new FileInputStream (filename);
          info.load (f);
    
          // ... 
    
        
 


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 ("Couldn't load file");
        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)

  void loadFromFile ()
  {
      // Create the FileDialog instance. 
      JFileChooser fc =  new JFileChooser();
      int returnCode = fc.showOpenDialog (null);
    
      File file = fc.getSelectedFile();
    
      info = new Properties ();
      try {
          FileInputStream f = new FileInputStream (file);
          info.load (f);

          firstname = info.getProperty ("firstname");
          firstnameTextF.setText (firstname);
          lastname = info.getProperty ("lastname");
          lastnameTextF.setText (lastname);
          networth = info.getProperty ("networth");
          networthTextF.setText (networth);
          address = info.getProperty ("address");
          addressTextA.setText (address);
      }
      catch (IOException e) {
          // Handle a file error gracefully with a dialog. 
          JOptionPane.showMessageDialog (this,
                                         "File does not exist",
                                         "Error dialog",
                                         JOptionPane.INFORMATION_MESSAGE);
      }
    
  }
  

Note:

  • Since message dialog's are common, Java has created a simple static method in JOptionPane:
          JOptionPane.showMessageDialog (this,
                                         "File does not exist",
                                         "Error dialog",
                                         JOptionPane.INFORMATION_MESSAGE);
      
  • To customize, use a JOptionPane instance.

    Note: 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.
 

Exercise 11.6: In this exercise, use your code from Ex.11.5 to add a dialog for the "About" option in the "Help" menu. Typically, "About" displays copyright and version 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).

Popup menus are a little more complicated than regular menus:

  • They are not explicitly added, but rather, displayed when the mouse is clicked.
  • We need to decide which of the other components should listen for popup-menu clicks.

Here are the relevant parts of the source file:

class NewFrame extends JFrame {

    // ...  

    JPopupMenu pm;            // The popup menu.  


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

        // A pop-up menu is handled a little different from the other menus. 
        makePopupMenu ();


        // ... 
    }


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

    JPanel firstName ()
    {
        JPanel subpanel = new JPanel ();

        // First, a label before the textfield. 
        JLabel L = new JLabel ("First Name");
        L.setFont (new Font ("SansSerif", Font.ITALIC, 20));
        subpanel.add (L);

        // Need to have each component listen for a Popup click. 
        L.addMouseListener (getMouseListener (L));

        // Create and add the textfield. 
        firstnameTextF = new JTextField (20);
        firstnameTextF.setForeground (Color.blue);
        subpanel.add (firstnameTextF);

        // Make the subpanel listen for mouseclicks too. 
        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());
                    // The "show" method requires the component reference, 
                    // which is why we store it in the variable c. 
            }  
        }

        return new PopupMouseListener (c);
    }


    // Create the popup menu and its listeners. 

    void makePopupMenu ()
    {
        // Note: pm is a top-level variable, which we will need to access elsewhere. 
        pm = new JPopupMenu ();

        // Move to "top" 
        JMenuItem topMenuItem = new JMenuItem ("Top");
        topMenuItem.addActionListener (
            new ActionListener () {
                public void actionPerformed (ActionEvent a)
	        {
                    top ();
	        }  
            }
        );
        pm.add (topMenuItem);

        // Move to "bottom" 
        JMenuItem bottomMenuItem = new JMenuItem ("Bottom");
        bottomMenuItem.addActionListener (
            new ActionListener () {
                public void actionPerformed (ActionEvent a)
	        {
                    bottom ();
	        }  
            }
        );
        pm.add (bottomMenuItem);

    }
  
    // Handle the "go to top" event. 

    void top ()
    {
        // Get the viewport out of the JScrollPane. 
        JViewport vp = sc.getViewport();
        // Set the new view. 
        vp.setViewPosition (new Point (0,0));
    }

    // Handle the "go to bottom" event. 

    void bottom ()
    {
        // Get the viewport out of the JScrollPane. 
        JViewport vp = sc.getViewport();
        // Set the new view. 
        Dimension D = vp.getViewSize();
        vp.setViewPosition (new Point (0,D.height));
    }

}
  

Note:

  • A JPopupMenu is similar to a JMenu in that we add JMenuItem's:
        pm = new JPopupMenu ();
    
        JMenuItem topMenuItem = new JMenuItem ("Top");
        topMenuItem.addActionListener (
            new ActionListener () {
                public void actionPerformed (ActionEvent a)
    	    {
                    top ();
    	    }  
            }
        );
        pm.add (topMenuItem);
        
  • We need to set up a MouseListener wherever we want the popup menu to appear:
    • In our case, that's each JLabel in the form.
    • Thus, the "first name" JLabel is given a MouseListener:
        JPanel firstName ()
        {
            JPanel subpanel = new JPanel ();
      
            // ... 
            JLabel L = new JLabel ("First Name");
            // ... 
      
            // Need to have each component listen for a Popup click. 
            L.addMouseListener (getMouseListener (L));
      
            // ... 
            subpanel.addMouseListener (getMouseListener(subpanel));
      
            return subpanel;
        }
             
    • 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());
                }  
            }
      
            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 JPopupMenu 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.

About scroll positions:



  • The scroll position of a JScrollPane can be adjusted using the associated "viewport".
        JViewport vp = sc.getViewport();
        // Set the new view. 
        vp.setViewPosition (new Point (0,0));
      
 


Sliders

 

In this section, we will learn how to use JSlider's with the time-honored example of setting a gnarly red-green-blue combination:

  • One JSlider for each of red, blue and green.
  • The setting on each slider 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 javax.swing.*;
import javax.swing.event.*;
import java.util.*;
import java.io.*;

class NewFrame extends JFrame {

    // Data. 

    JSlider               // One scrollbar for each color. 
      redSlider,
      blueSlider,
      greenSlider;

    int                   // Intensity value for each color. 
      redValue = 0,
      blueValue = 0,
      greenValue = 0;

    JPanel drawingArea;   // To display the mixed color. 


    // Constructor. 

    public NewFrame (int width, int height)
    {
        this.setTitle ("RGB combination");
        this.setResizable (true);
        this.setSize (width, height);

        Container cPane = this.getContentPane();
        // cPane.setLayout (new BorderLayout()); 

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

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

        // A local Listener for the sliders 
        class ColorChangeListener implements ChangeListener {
            public void stateChanged (ChangeEvent e)
   	    {
   	        adjust ();
	    }
        }

        // Red one. 
        redSlider = new JSlider(JSlider.HORIZONTAL, 0, 255, 0);
        redSlider.addChangeListener (new ColorChangeListener());
        p.add (redSlider);

        // Green one. 
        greenSlider = new JSlider (JSlider.HORIZONTAL, 0, 255, 0);
        greenSlider.addChangeListener (new ColorChangeListener());
        p.add (greenSlider);

        // Blue one. 
        blueSlider = new JSlider (JSlider.HORIZONTAL, 0, 255, 0);
        blueSlider.addChangeListener (new ColorChangeListener());
        p.add (blueSlider);

        cPane.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);
	        }  
            }
        );
        cPane.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. 
        redValue = redSlider.getValue();
        greenValue = greenSlider.getValue ();
        blueValue = blueSlider.getValue ();

        // Set the color in each scrollbar. 
        redSlider.setBackground (new Color (redValue, 0, 0));
        greenSlider.setBackground (new Color (0, greenValue, 0));
        blueSlider.setBackground (new Color (0, 0, blueValue));

        // Create the composite color. 
        Color newColor = new Color (redValue, greenValue, blueValue); 
        drawingArea.setBackground (newColor);
        drawingArea.repaint ();
    }

}


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

Note:

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


  • A ChangeListener is required to listen for slider 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 slider example above 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 JFrame {

    // ...  

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

       // A KeyListener for the panel 
       drawingArea.addKeyListener ( getKeyListener() );

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

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

       // A KeyListener for the blue scrollbar.  
       blueSlider.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. 
                    redSlider.setValue (Math.max (0, redValue-10));
	            adjust ();
	        }
	        else if (k.getKeyCode() == KeyEvent.VK_RIGHT) {
  	            // Right arrow. 
		    redSlider.setValue (Math.min (255, redValue+10));
		    adjust ();
		}
            }
        };
    }

}
  

Note:

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

  • 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.
 

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


Additional Swing topics

 
Swing topics we have not covered:

  • Borders:
    • How to create borders for components, e.g., etched, bevel, titled.
    • Use the javax.swing.border package.
  • Look and Feel:
    • How to change the appearance of widgets.
    • Use the static method javax.swing.UIManager.setLookAndFeel() and classes in javax.swing.plaf.
  • More containers:
    • JSplitPane - use this for a movable boundary in a split panel.
    • JTabbedPane - use this to create a "tabbed" layout.
  • More layout managers:
    • GridBagLayout - the most flexible (and most complicated) layout manager.
    • BoxLayout - a simpler version of GridBagLayout.
  • Icons:
    • Almost every Swing widget can include icons.
    • Use javax.swing.ImageIcon.
  • Toolbars and Tooltips:
    • javax.swing.JToolBar - for removable toolbars.
    • javax.swing.JToolTips - for balloon-like tooltips.
  • Text:
    • JEditorPane - for styled documents that can be user-edited.
    • javax.swing.text.html - package for HTML editing and display.
  • Advanced components:
    • JTable - widget for generic tables (e.g., to build a spreadsheet).
    • JTree - widget for tree-structured data (e.g., to explore file directories).
  • Miscellaneous:
    • File filters - use these along with file dialogs to restrict files displayed.
    • JColorChooser - a color selection dialog.
    • JProgressBar - a progress bar to show how much progress has been made so far (e.g., in uploading a file).
    • Printing.
    • Sophisticated graphics with Graphics2d.



© 1998, Rahul Simha (revised 2017)