GUI 6 - DogYears - Listeners

Purpose of this lesson: New Java classes, interfaces, and methods

This program is a model for many programming problems

A lot of problems, especially beginning programming assignments, fit into this form: one or more input fields, a button that performs the computation, and output fields that display the result. You should be able to take this program and modify it to solve other similar problems without too much work.

Java Practice: Listeners

All GUI systems, not just Java, call a method when the user does something that demands the program's attention, eg clicking a button. The details of the connection between the GUI component and the called method method vary from language to language. Java uses a pure object-oriented approach to implementing what they call listeners.

There are several kinds of listeners in Java, depending on what the user does, but the most common is called an action listener, which is used with buttons, menu items, etc. An action listener is added to the button in the example below with the following statement.

convertBtn.addActionListener(new ConvertBtnListener());

Terminology note. The word "interface" is used in several different ways in Java.

  1. A user interface such as a GUI or console interface.
  2. An Application Programming Interface (API) are the classes and methods defined for use by others.
  3. The Java programming unit called interface that is a list of methods that must be defined by any class that implements it.

ActionListener programming interface. An action listener is an object that implements the ActionListener programming interface. The ActionListener interface requires that one method, actionPerformed, be defined. Listener classes are usually defined as inner classes (classes inside other classes). In the DogYears example below you can see that objects of the inner class ConvertBtnListener can be used as action listeners because the class implements ActionListener.

class ConvertBtnListener implements ActionListener

If a class says that it implements an interface, then the definition of the class must contain the required method definitions, and you can see that actionPerformed is indeed defined.

What happens if you don't define the methods. If a class declares that it implements an interface, but doesn't define the required methods, you'll get an error message. The message has improved in recent versions of Java, but if it says something about abstract classes you can be sure you either forgot to define the method, misspelled the method name, have the wrong return type or number or type of parameters. The method must be defined exactly as required by the interface. Don't try to get rid of the error message by making your class abstract (a less common design option).

Why use an inner class? You want to tell Java that it should call a particular method when the button is clicked, but there is no natural way to pass a method as a parameter in Java. The solution is to pass an object that defines the actionPerformed method. To make an object that has this method, you must first define a class (methods can only be defined in classes).

The actionPerformed method in this class must be able to get and set the values in the text fields or other input and output areas of the GUI. A simple, common solution is to define this listener class inside the JFrame subclass. Any method inside an inner class can refer to instance variables in the outer class. The example below illustrates this.

Source code: DogYears GUI and logic

DogYears user interface

  1 
  2 
  3 
  4 
  5 
  6 
  7 
  8 
  9 
 10 
 11 
 12 
 13 
 14 
 15 
 16 
 17 
 18 
 19 
 20 
 21 
 22 
 23 
 24 
 25 
 26 
 27 
 28 
 29 
 30 
 31 
 32 
 33 
 34 
 35 
 36 
 37 
 38 
 39 
 40 
 41 
 42 
 43 
 44 
 45 
 46 
 47 
 48 
 49 
 50 
 51 
 52 
 53 
 54 
 55 
 56 
 57 
 58 
 59 
 60 
 61 
 62 
 63 
 64 
 65 
 66 
 67 
 68 
 69 
// File   : gui-tutorial/DogYears2.java
// Purpose: Use textfields and button to convert dog to human years.
//          A button listener has been added in this version.
//          No error checking on input values.
// Author : Fred Swartz, 2006-11-09,  Placed in public domain.

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;  // Needed for ActionListener

//////////////////////////////////////////////////////// class DogYears2
class DogYears2 extends JFrame {
    //======================================================== constants
    private static final int DOG_YEARS_PER_HUMAN_YEAR = 7;      //Note 1

    //=============================================== instance variables
    private JTextField _humanYearsTF = new JTextField(3);       //Note 2
    private JTextField _dogYearsTF   = new JTextField(3);

    //====================================================== constructor
    public DogYears2() {                                        //Note 3
        // 1... Create/initialize components
        JButton convertBtn = new JButton("Convert");  //Note 4
        convertBtn.addActionListener(new ConvertBtnListener()); //Note 5

        _dogYearsTF.addActionListener(new ConvertBtnListener());
        _humanYearsTF.setEditable(false);


        // 2... Create content panel, set layout
        JPanel content = new JPanel();
        content.setLayout(new FlowLayout());

        // 3... Add the components to the content panel.
        content.add(new JLabel("Dog Years"));
        content.add(_dogYearsTF);              // Add input field
        content.add(convertBtn);               // Add button
        content.add(new JLabel("Human Years"));
        content.add(_humanYearsTF);            // Add output field

        // 4... Set this window's attributes, and pack it.
        setContentPane(content);
        pack();                               // Layout components.
        setTitle("Dog Year Converter");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);          // Center window.
    }

    ////////////////////////////////////////////////// ConvertBtnListener
    class ConvertBtnListener implements ActionListener {         //Note 6
        public void actionPerformed(ActionEvent e) {
            //... Get the value from the dog years textfield.
            String dyStr = _dogYearsTF.getText();                //Note 7
            int dogYears = Integer.parseInt(dyStr);              //Note 8

            //... Convert it - each dog year is worth 7 human years.
            int humanYears = dogYears * DOG_YEARS_PER_HUMAN_YEAR; //Note 9

            //... Convert to string and set human yrs textfield
            _humanYearsTF.setText("" + humanYears);              //Note 10
        }
    }

    //====================================================== method main
    public static void main(String[] args) {
        DogYears2 window = new DogYears2();
        window.setVisible(true);
    }
}

Notes

  1. Your program should not contain "magic numbers". Instead, define constants with meaningful names.
  2. This declares and initializes a text field to be approximately 3 characters wide.
  3. The GUI constructor typically performs the following chores: (1) Finish initializing components (creating, setting attributes, adding listeners, ...). (2) Create a content pane and set the layout. (3) Add the components. (4) Set frame attributes, including the content pane, and pack it to do the layout.
  4. This declares and initializes a "Convert" button.
  5. This creates an association between the button and an object. When the button is clicked, it will call the actionPerformed method of that object.
  6. This is one of most common ways to create a listener - define an inner class that "implements ActionListener", and in that class define the actionPerformed() method.
  7. Get the text from a JTextField with a call to its getText() method. This returns a string, which in this case must be converted to a number before it can be used in the computation.
  8. If this can't be converted, it will throw an exception. You'll learn how to handle exceptions later.
  9. This is the essential "logic" or "model" of the problem. It's so simple that I put it here in the listener. It would be better in a separate class, or at least a separate method, Later examples with more logic will separate the logic/model...
  10. Set the value of a JTextField by calling its setText() method and passing a string to it. Concatenating an empty string is a common idiom to convert a number to String.

Commentary

You can write this without subclassing JFrame. Look at Commentary - Build window in main() to see this program rewritten in another style.

Programming problems

[TODO]