4-3 Labels and Buttons

Now that you know how to draw beautiful shapes, let’s turn our attention to the JLabel and JButton Swing classes.

JLabel Widgets

A JLabel can be text or an image in a GUI.  They are most often used to display information or instructions to the user.  As our GUIs become more complex it makes sense to organize code such that our main() test program is as focused as possible and we move most of our logic into a custom class that inherits from JFrame.

Let’s start with a “Hello Java!” example:

Since we are extending the JFrame class, the first thing we do in our JLabelFrame constructor (line 9) is to call the parent (JFrame) constructor to set the window title.

There are different ways that widgets can be arranged in a GUI.  The default layout for a JFrame is called BorderLayout.  BorderLayout has some restrictions that we’ll get into later so, on line 14, I’m switching the layout to FlowLayout instead.  FlowLayout is very flexible and allows us to add as many widgets as we like to the JFrame.  It automatically arranges widgets from left to right, top to bottom in the order we add() them.  If we resize the window, the FlowLayout manager will automatically “re-flow” the widgets so that as many as possible are visible at one time.

Line 17 is an example of creating a text JLabel.  Lines 21 and 22 show creating an ImageIcon object to load an image from disk, and then creating a graphical JLabel.

Here is a test program to instantiate and configure our JLabelFrame.  As mentioned earlier, our goal is to keep this test class as lean as possible.

JButton Widgets and Inner Classes

JButton widgets are instantiated and added to a window in much the same way as JLabels.  The biggest difference is that buttons are interactive!  When a user interacts with a widget, like clicking a button, this is called an event.  To respond to user events we need to write an event listener class that contains code that is executed when an event occurs.

In Java, an event listener is written as an inner class.  An inner class is a class that’s coded within the body of another class.  Consider this:

Here the OuterClass follows all of the Java rules we’ve learned up to this point, but the InnerClass follows slightly different rules.  First, an inner class may not be declared as public and therefore cannot be accessed from outside the OuterClass.  This scope makes sense because the event listener code will be related to a specific widget created in the OuterClass only.  Second, the InnerClass has direct access to all public and private instance variables and methods defined in the OuterClass, and can define it’s own instance variables and methods.  And finally, the InnerClass is not allowed to contain any static variables or methods.

Now let’s convert our previous JLabel example into a JButton example with event handling:

On line 6 we import the ActionListener interface which defines a method called actionPerformed() that is called automatically when a button event occurs.  Our overridden actionPerformed() method (lines 40 to 50 receives an ActionEvent parameter from the JVM which contains useful information about the event.  For example, on line 44 I use its getSource() method to figure out which of the two buttons triggered the event.

On lines 23 and 25, I instantiate our ButtonEventListener and associate it with our textButton.  On line 32, the same ButtonEventListener object is associated with our imageButton.  There are different ways to do this, you might choose to create two independent (inner) event listener classes, one for each button that do completely different things.  In this case, I’m handling events for both buttons using the same event listener.

Here is the test class:

Anonymous Inner Classes

In the previous example, the name of the event listener class that implemented the ActionListener interface (line 37) was called ButtonEventListener. It’s also possible (and common in Java) to create an inner class that implements the ActionListener interface without naming the event listener class.  This kind of class definition is called an anonymous inner class.

Consider the JButtonFrame constructor again, this time using an anonymous inner class:

Lines 9 to 21 show how to define and use an anonymous inner class. On line 9 we still have the eventListener reference variable to refer to the ActionListener object that we are instantiating, this allows the second button to share the same event handler (line 29) as before.  What’s different is that the inner class is defined within the JButtonFrame() constructor, and we no longer have to name a class to implement the ActionListener interface.

If an event listener object will only be used by one widget, and never shared, it’s possible to go a step further.  Let’s change the example code one more time to demonstrate:

Above we no longer need to define a reference variable to keep track of (and share) one ActionListener object.  We are actually instantiating two independent ActionListener objects within the addActionListener() parameter list for each button.  At first it may feel strange defining a class within a parameter list, but this is a common short cut in Java.  Since each ActionListener object is associated with only one button, we no longer need to figure out which button might have triggered the event and this results in more concise actionPerformed() code.

For simple graphical user interfaces, the two forms of anonymous inner classes shown above are widely used in Java.  However, as interfaces become more complex using regular (named) inner classes, as in our first JButton example, will result in code that is better designed and easier to read and maintain.  Imagine how long your constructor method code would become if you have dozens of interactive widgets all with their own anonymous inner classes for event handling!

You Try!

  1. Write a GUI application to help track student success.  Your solution should look something like this.  Include two buttons to keep track of the number of passes and fails, with a label to give an ongoing status update.  Use a regular (not anonymous) inner class to take care of the event handling for both buttons.  Hint: the JLabel class has a method that you can use to update the text on a JLabel object any time after it has been instantiated.
  2. Modify your solution to use an anonymous inner class for event handling instead.  Which method do you prefer more?  Why?
  3. It’s easy to add mouse-over text to JLabel and JButton widgets.  Research the setToolTipText() method and add this feature to both buttons in your student success tracker.