Components and containers (Section 12.2)
The JFrame class
Containers
Components
Interfaces (Section 12.3, part 2)
Defining an interface
Implementing an interface
Interface variables
Event listeners (Section 12.3)
Creating a listener
Registering a listener
Textbook: Section 12.2
Many common programs involve graphical user interfaces. We're going to look at Java's libraries for building GUIs, called Swing. (There's an older library Java used for this, called AWT. This is what the textbook covers. We're going to look at Swing in this class, though.)
The first class we'll look at is the JFrame, which represents a window. Consider the following simple program.
This particular program isn't very useful - it just creates a frame and displays it, effectively showing an empty window.import java.awt.*; // we'll include these three import lines in import java.awt.event.*; // all our programs using Swing. import javax.swing.*; public class EmptyWindow extends JFrame { public EmptyWindow() { pack(); } public static void main(String[] args) { EmptyWindow window = new EmptyWindow(); window.show(); } }
A JFrame has just a few methods worth talking about for now.
A Container is an object used for holding components. (Examples of components include buttons and text fields.) For the moment, we're just going to pay attention to one of its methods.
BorderLayout.NORTH | BorderLayout.EAST |
BorderLayout.SOUTH | BorderLayout.WEST |
BorderLayout.CENTER |
Finally, there are the components. The first one of these we'll see is the JButton, which represents a button on the screen. For the moment, we'll just worry about the constructor method.
Another type of component is the text field, where the user can type something. The JTextField class is
Here's a program that creates a window containing two buttons and a text field. We're going to work on making the buttons do something soon. For the moment, all the program does is display the components.
When executed, this makes a window containing a text field sandwiched by a button labeled ``Increment'' at the top and a button labeled ``Quit'' below.import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ButtonExample extends JFrame { JButton incr_button; JButton quit_button; JTextField number_field; public ButtonExample() { incr_button = new JButton("Increment"); quit_button = new JButton("Quit"); number_field = new JTextField(); Container contents = getContentPane(); contents.add(incr_button, BorderLayout.NORTH); contents.add(number_field, BorderLayout.CENTER); contents.add(quit_button, BorderLayout.SOUTH); pack(); } public static void main(String[] args) { (new ButtonExample()).show(); } }
Textbook: Section 12.3, part 2
Before we continue, we need to discuss interfaces. An interface is basically just a set of methods. Any class that hopes to implement the interface must implement all of the methods in the interface's set.
Despite the name, interfaces are not specific to graphical user interfaces! They're a general Java concept, that we just happen to be introducing with graphical user interfaces.
Defining an interface looks a lot like defining a class, except that we can only list instance methods, and none of the instance methods have any bodies.
For example, we might have defined the following interface in our Breakout game.
This was an interface that both the Ball and the Paddle classes implemented.public interface MovingObject { public void step(DrawableFrame f); public void draw(Graphics g); }
When you define a class that implements the interface, you need to say so up front with an implements clause.
Any class that has such an implements clause has to define all of the methods defined in the interface. Otherwise, there's a compiler error.public class Ball implements MovingObject { //... public class Paddle extends Block implements MovingObject { //...
(An astute observer will notice that in Breakout, Part III, we redefined Ball's step method to return a boolean. That class didn't really implement the MovingObject interface we explained, and the compiler would refuse to let us get away with it.)
What makes interfaces useful is that they are types, too. The following would be completely legitimate.
Naturally, you can never create an instance of an interface.DrawableFrame frame = new BreakoutFrame(); MovingObject mover = new Ball(); mover.step(frame); mover.draw(frame.getGraphics());
So, interfaces in a certain sense allow you to have a class have two ``superclasses.'' Only one of these is a true Java class, but the behavior as far as converting between types makes both the implemented interface and the real parent class behave like superclasses.
Textbook: Section 12.3
So how can we have our button do something? We're going to have to use an interface - namely, the ActionListener class in the java.awt.event package, which includes the following method.
The ActionEvent paremeter specifies information about the event that has occurred. For the moment, we're just going to worry about its getSource() method, which returns the Component on which the action event was performed.
had an actionIn order to define what to do for the button, we're going to need an object that implement this interface. Inside that object's actionPerformed() method, we'll have some code that say what to do.
The other step in setting up the program to handle events is to register the ActionListener object with the component. In our case, we'll want the addActionListener() method in the JButton class. It takes takes an ActionListener as an argument and sets up the button so that, when clicked, it calls the actionPerformed() method in that ActionListener.
The changes from before are drawn in boldface here.
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class ListeningExample extends JFrame implements ActionListener { JButton incr_button; JButton quit_button; JTextField number_field; public ListeningExample() { incr_button = new JButton("Increment"); quit_button = new JButton("Quit"); number_field = new JTextField(); incr_button.addActionListener(this); quit_button.addActionListener(this); Container contents = getContentPane(); contents.add(incr_button, BorderLayout.NORTH); contents.add(number_field, BorderLayout.CENTER); contents.add(quit_button, BorderLayout.SOUTH); pack(); } public void actionPerformed(ActionEvent evt) { if(evt.getSource() == incr_button) { int i = Integer.parseInt(number_field.getText()); number_field.setText("" + (i + 1)); } else if(evt.getSource() == quit_button) { System.exit(0); // The System.exit class method halts entire program } } public static void main(String[] args) { (new ListeningExample()).show(); } }
The important changes here are that we've added two lines in the ListeningExample constructor method registering the ListeningExample object to ``listen'' to actions being performed on its two buttons. That is, when somebody clicks on either button, it will call the actionPerformed() method on the constructed ListeningExample object.
The other important change is the addition of the actionPerformed() method. What it does is first to see which component triggered the action - that is, whether the Increment or Quit button was clicked. It performs an action based on this.