- Window Interfaces Using Swing Objects
- Basic Swing Details
- Buttons and Action Listeners
- Container Classes
- Text I/O for GUIs
- Summary
Buttons and Action Listeners
I claim not to have controlled events, but confess plainly that events have controlled me.
Abraham Lincoln, Letter to A. G. Hodges (April 4, 1864)
So far the GUIs we have produced using Swing have included no actions. They just sat there and displayed some text. The only action they took was to go away in response to clicking the close-window button. In this section, we start to show you how to design GUIs that do things like changing color, changing text, or some more complicated actions. A button is simply a component in a GUI that looks like a button and that does something when you click it with your mouse. You create buttons in a way that is very similar to how you create labels. You add buttons to a JFrame in the same way that you add labels to a JFrame, but there is also something very new about buttons. You can associate an action with a button, so that when the user clicks the button with a mouse, the GUI performs some action. An example should make the details clear.
Programming Example: Adding Buttons
Display 12 contains a program that creates a window with two buttons: labeled "Red" and "Green". When the program is run, the window shown in Display 12 is displayed. If you click the button marked "Red" with your mouse, the color of the window changes from blue (or whatever color it is) to red. If you click the button labeled "Green". the color of the window changes to green. That is all the program does (but as you can see, you are gradually learning to build more complicated windowing interfaces). To end the program and make the window disappear, you click the close-window button.
Display 12A GUI with Buttons Added
import javax.swing.*; import java.awt.*; import java.awt.event.*; /*********************************************** *Simple demonstration of putting buttons in a JFrame. ***********************************************/ public class ButtonDemo extends JFrame implements ActionListener { public static final int WIDTH = 300; public static final int HEIGHT = 200; /************************************************* *Creates and displays a window of the class ButtonDemo. *************************************************/ public static void main(String[] args) { ButtonDemo buttonGui = new ButtonDemo(); buttonGui.setVisible(true); } public ButtonDemo() { setSize(WIDTH, HEIGHT); addWindowListener(new WindowDestroyer()); setTitle("Button Demo"); Container contentPane = getContentPane(); contentPane.setBackground(Color.blue); contentPane.setLayout(new FlowLayout()); JButton stopButton = new JButton("Red"); stopButton.addActionListener(this); contentPane.add(stopButton); JButton goButton = new JButton("Green"); goButton.addActionListener(this); contentPane.add(goButton); } //It will take several subsections to fully explain this program. //The explanation does not end until the end of the subsection //entitled Action Listeners and Action Events public void actionPerformed(ActionEvent e) { Container contentPane = getContentPane(); if (e.getActionCommand().equals("Red")) contentPane.setBackground(Color.red); else if (e.getActionCommand().equals("Green")) contentPane.setBackground(Color.green); else System.out.println("Error in button interface."); } }
Much of what appears in Display 12 is already familiar to you. The class ButtonDemo is a derived class of the class JFrame and so it is a window interface similar to the ones we have already seen in this article. A window listener of the class WindowDestroyer is added with the method addWindowListener as in previous examples. The size is set, and the content pane is obtained, with getContentPane as in previous examples. We use a layout manager as we did in the previous example (Display 10), although this time we use the FlowLayout manager class.
What is new in Display 12 is the use of button objects of the class JButton and a new kind of listener class. We will discuss buttons and the new listener class in the next few subsections.
Buttons
A button is a GUI component that looks like a button and that can do something when you click it with your mouse. In this subsection we tell you how to add buttons to your GUI. In the next subsection we tell you how to specify what happens when the button is clicked.
A button object is created in the same way that any other object is created, but you use the class JButton when you want buttons. For example, the following example from Display 12 creates a button:
JButton stopButton = new JButton("Red");
The argument to the construct, in this case "Red", is a string that will be written on the button when the button is displayed. The string argument to the constructor, in this example "Red", specifies the string that will appear on the button when it is displayed on the screen. If you look at the GUI in Display 12, you will see that the two buttons are labeled "Red" and "Green".
The button is added to the content pane with the following:
contentPane.add(stopButton);
There is no second argument to the method add because we are using the FlowLayout manager class. If we had instead used the BorderLayout manager class, then we would have used some second argument, such as BorderLayout.NORTH.
In the next subsection we explain the lines from Display 12 involving the method addActionListener.
Action Listeners and Action Events
When you click a button with your mouse (or activate some other item in a GUI), it creates an object known as an event and sends the object to another object (or objects) known as the listener. This is called firing the event. The listener then performs some action. When we say that the event is "sent" to the listener object, what we really mean is that some method in the listener object is invoked with the event object as the argument. This invocation happens automatically. Your Swing GUI class definition will not normally contain an invocation of this method. However, your Swing GUI class definition does need to do two things: First for each button, it needs to specify what object(s) are listeners that will respond to events fired by that button; this is called registering the listener. Second, your GUI class definition(s) must also define the method(s) that will be invoked when the event is sent to the listener. Note that these methods will be defined by you, but in normal circumstances, you will never write an invocation of these methods. The invocations will take place automatically.
The following line from Display 12
stopButton.addActionListener(this);
registers this as a listener to receive events from the button named stopButton. A similar statement also registers this as a listener to receive events from the button named goButton. Because the argument is this, it means that this (the class ButtonDemo itself) is the listener class. Recall that within the definition of a class, an object of that class is called this. The class ButtonDemo is itself the listener class for the buttons inside of ButtonDemo. (To be a bit more precise, this means that each object of the class ButtonDemo is the listener for the buttons in that object.) Next, we explain how to make a class, such as ButtonDemo, into a listener class for events fired by buttons.
Different kinds of components require different kinds of listener classes to handle the events they fire. A button fires events known as action events, which are handled by listeners known as action listeners.
An action listener is an object of type ActionListener. ActionListener is not a class, but is a property that you can give to any class you define. (These properties, such as ActionListener, are known as interfaces.) To make a class into an ActionListener, you need to do two things:
You add the phrase implements ActionListener to the beginning of the class definition, normally at the end of the first line.
You define a method named actionPerformed.
In Display 12, we made the JFrame class named ButtonDemo into an ActionListener in just this way. In what follows, we reproduce an outline of the definition of the class ButtonDemo (with the omitted sections indicated by three dots):
public class ButtonDemo extends JFrame implements ActionListener { . . . public void actionPerformed(ActionEvent e) { . . . } . . . }
We could have defined a separate class that did nothing but handle button events, but it's more convenient to make the window class ButtonDemo into the Action-Listener that will handle button events. This is convenient because the button events are supposed to change the window, and the easiest way to change a window is by a method within the window itself.
Now, suppose we create an object, buttonGui, of the class ButtonDemo as follows:
ButtonDemo buttonGui = new ButtonDemo();
The this parameter in the definition of the class ButtonDemo refers to buttonGui. So buttonGui is the action listener for the two buttons inside of buttonGui. So when a button is clicked, the method actionPerformed will be automatically invoked with the event fired by the button as the argument to actionPerformed. All that is left to explain is how the method actionPerformed works. Let's continue with our object buttonGui of the class ButtonDemo.
If you click one of the buttons inside of buttonGui with your mouse, that sends an action event to the action listener for that button. But buttonGui is the action listener for the buttons in buttonGui, so the action event goes to buttonGui. When an action listener receives an action event, the event is automatically passed to the method actionPerformed. The method actionPerformed is typically a branching statement that determines what kind of action event was fired and then performs some appropriate action. Let's look at the code for the method actionPerformed in the class ButtonDemo in Display 12. For convenience, we reproduce the definition in what follows:
public void actionPerformed(ActionEvent e) { Container contentPane = getContentPane(); if (e.getActionCommand().equals("Red")) contentPane.setBackground(Color.red); else if (e.getActionCommand().equals("Green")) contentPane.setBackground(Color.green); else System.out.println("Error in button interface."); }
In this case, the method actionPerformed needs to know whether the action event came from the button labeled "Red" or the button labeled "Green". If e is an action event that was fired by clicking a button, then e.getActionCommand() returns the string written on the button; in this case it returns either "Red" or "Green". So, all that the method actionPerformed needs to do is to see if e.getActionCommand() is "Red" or "Green" and perform the appropriate action for that button. Note that e.getActionCommand() is an object of the class String. The class String has a method equals that can be used to check to see if e.getActionCommand() is equal to "Red" or "Green" (or any other string). The method invocation
e.getActionCommand().equals(String_Argument)
returns true if e.getActionCommand() is equal to the String_Argument, and returns false otherwise. Thus, the following code tests to see if e.getActionCommand() is equal to "Red" or "Green" and changes the color of the GUI accordingly:
if (e.getActionCommand().equals("Red")) contentPane.setBackground(Color.red); else if (e.getActionCommand().equals("Green")) contentPane.setBackground(Color.green); else System.out.println("Error in button interface.");
The final else clause should never need to be executed. It is just there so that we would get a warning, if we made some unnoticed mistake in the code.
Gotcha: Changing the Parameter List for actionPerformed
When you make a class into an action listener, the header for the method actionPerformed is determined for you. It must have exactly one parameter, and that parameter must be of type ActionEvent, as in the following:
public void actionPerformed(ActionEvent e)
If you change the type of the parameter or if you add (or subtract) a parameter, you will not have given a correct definition of an action listener. (Although it would be rather questionable style, you can overload the method name actionPerformed so you have multiple versions of the method actionPerformed, each with a different parameter list. But only the one shown above has anything to do with making a class into an action listener.)
The only thing you can change is the name of the parameter e because it is just a "place holder." So, the following change is acceptable:
public void actionPerformed(ActionEvent theEvent)
Of course, if you make this change, then inside the body of the method actionPerformed, you will use the identifier theEvent in place of the identifier e.
Programming Tip: Code Look and Actions Separately
You can simplify writing the code for a Swing GUI into two major parts: coding what the GUI looks like on the screen and coding the actions of the GUI. For example, consider the program in Display 12. Your first version of this program might use the following definition of the method actionPerformed:
public void actionPerformed(ActionEvent e) {}
With this "do-nothing" version of the method actionPerformed, your program will run and will show a display on the screen just as shown in Display 12. If you press either of the two color change buttons nothing will happen, but you can use this phase of coding to adjust details, such as which button is listed first. (Note that the close-window button does, however, work.)
After you get the GUI to look like you want it to look, you can then define the action parts of the GUI, typically the method actionPerformed. This breaks the task of coding the Swing GUI into two smaller tasks: coding the appearance of the GUI and coding the actions of the GUI. In this case that may not seem like a big deal, but on a complicated Swing GUI, each of these two tasks can be formidable, but reasonable, while coding the entire GUI as one piece can be maddeningly difficult. In such cases, this dividing into two parts can be a great help.
If you include the phrase implements ActionListener at the start of your JFrame definition, then you must include some definition of the method actionPerformed. A "do nothing" or "do little" method definition, such as
public void actionPerformed(ActionEvent e) {}
is often called a stub. Using stubs is a good programming technique in many contexts not just in Swing programs.
Alternatively, when doing your first version of a Swing GUI like the one in Display 12, you could omit the definition of the method actionPerformed completely, provided you also omit the phrase implements ActionListener and omit the invocations of addActionListener.
Java Tip: Use the Method setActionCommand
When you click a button with your mouse, it fires an event e known as an action event. The event e normally goes to the method actionPerformed of the action listener(s) for that button. The method actionPerformed needs to find out what button was clicked when it gets the event e. When discussing the class ButtonDemo in Display 12, we said that the method invocation e.getActionCommand() returns the string written on the button. That was a slightly simplified explanation. The invocation e.getActionCommand() returns a string known as the action command for the button. The default action command is the string written on the button, but if you want to, you can specify that a different string be the action command for a button.
For example, in Display 12, we created the stopButton as follows:
JButton stopButton = new JButton("Red");
If we do nothing more, the action command for the stopButton will be "Red". However, if you want, you can change the action command to some other string, such as "Stop". You would do so as follows:
stopButton.setActionCommand("Stop");
The method actionPerformed would then check for the string "Stop" rather than the string "Red".
You may eventually find yourself in a situation where you want the same string written on two different buttons. In such a case, you can distinguish the two buttons by using setActionCommand to give them different action commands.