Events

Contents

 

 

Event-Listener

 

Action Events

 

Mouse Events

 

Key Events

 

Andere Events

 

Adapter Class

 

Anonymous classes

 

Practice exercises

Event-Listener

As we saw in the chapter on windows, managing a user interface is based on the concept of events . An event is usually a user-initiated action, such as pressing a key or clicking a mouse. However, other systems also provide different types of events, such as a timer that triggers an event at certain intervals. Another possibility would be the arrival of a data packet from the internet.

The question arises as to when events are processed. There are three possibilities.

  1. Option 1: Events are processed sequentially . In this case, the events are placed in an event queue, and the next processing step is only initiated once the previous one is complete. This is how processing works in Java. The disadvantage is that lengthy processing times can block user interaction.
  2. Option: Events are handled in separate threads . The programmer then has to ensure correct synchronization of these threads. This is not an easy task. For most user input, this would simply mean that the program would have to wait until the processing of the last event is complete.
  3. Option: There are multiple queues . This is a middle ground between the two extremes. In Java, there is only one queue for user input. Other event handlers are implemented separately by the programmer, which corresponds to a different queue.

So far, we have only encountered one type of event handling, namely the processing of the Repaint event in a graphical component. This was achieved by overriding the paint method. This type of processing by overriding is now used very restrictively in Java.

Java's event handling relies on objects that can receive events. These objects are called event listeners . They implement certain interfaces and thus certain methods that handle the events. Furthermore, the objects must register with the event creators. Multiple listeners can register with a single creator.

As a side effect of the listener model, the implementation of the user interface elements and their arrangement are separated from the program logic, which lies in the processing of events. This allows the appearance of the program and its functionality to be considered separately.

Action Events

As an example, we will consider the event of a button being pressed. A JButton is inserted into a window. A method that terminates the program acts as an event listener.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

class CloseButton extends JButton implements ActionListener
{
    public CloseButton ()
    {
        super("Close");
        addActionListener(this);
    }

    @Override
    public void actionPerformed (ActionEvent e)
    {
        System.exit(0);
    }
}

public class ButtonTest
{
    public static void main (String args[])
    {
        JFrame F = new JFrame();
        F.setTitle("Test");
        F.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Register as an Action Listener:
        F.add(new CloseButton());

        // Show:
        F.pack();
        F.setLocationRelativeTo(null);
        F.setVisible(true);
    }
}

As you can see, this example creates a new instance of an ActionListener to react to the button being pressed. This instance must then be registered as an ActionListener with the button. If the button is pressed, it notifies all registered ActionListeners by calling their actionPerformed() method. An instance of ActionEvent, containing further information about the event, is passed as a parameter.

One button can control multiple ActionListeners. An ActionListener can remove itself from the list of event receivers using the `removeActionListener()` function.

This example also shows how the user interface structure is separated from event handling. Ultimately, any class can act as a listener that processes the event.

The example can also be organized differently by writing a custom JFrame class. This class can then register itself as an event listener and query the event source in a routine. It looks like this.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

class TestFrame extends JFrame
        implements ActionListener
{
    JButton Close; // The close button

    /**
     * Initialize the window. It contains only a close button.
     */
    public TestFrame ()
    {
        setTitle("Test");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Insert button:
        Close = new JButton("Close");
        Close.addActionListener(this);
        add(Close);

        pack();
    }

    /**
     * The ActionListener, currently only for the button
     */
    @Override
    public void actionPerformed (ActionEvent e)
    {
        if (e.getSource() == Close)
        {
            dispose();
        }
    }
}

public class Test
{
    public static void main (String args[])
    {
        JFrame F = new TestFrame();
        F.setLocationRelativeTo(null);
        F.setVisible(true);
    }
}

The main program now simply creates an instance of TestFrame. This class handles everything itself, including managing the events from its buttons. Since we might want to add multiple buttons, we test where the event originates.

Mouse Events

As an example of mouse events , we will react to a click inside a TestCanvas. The goal is simply to output the coordinates of the click.

The interface here is called MouseListener and implements five methods. We register the canvas itself as a MouseListener. The event is called MouseEvent and has methods such as getX() and getY(), which can be used to retrieve the coordinates of the click.

In this example, the component itself registers as an event listener.

import java.awt.Canvas;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;

class TestCanvas extends Canvas implements MouseListener
{
    public TestCanvas ()
    {
        addMouseListener(this);
    }

    // MouseListener methods

    @Override
    public void mousePressed (MouseEvent e)
    {
        System.out.println(e);
    }

    @Override
    public void mouseReleased (MouseEvent e)
    {
        System.out.println(e);
    }

    @Override
    public void mouseClicked (MouseEvent e)
    {
        System.out.println(e);
        System.out.println(
                "Mouse click at " + e.getX() + "," + e.getY());
    }

    @Override
    public void mouseEntered (MouseEvent e)
    {
        System.out.println(e);
    }

    @Override
    public void mouseExited (MouseEvent e)
    {
        System.out.println(e);
    }
}

public class Test
{
    public static void main (String args[])
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(new TestCanvas());
        f.setSize(300, 300);
        f.setVisible(true);
    }
}

Running this example shows that `mouseClicked()` is only called when the mouse is pressed and released at the same point. It also shows that double-clicks are difficult to detect. We'll have to postpone the necessary techniques for that.

To track mouse movement, you need a MouseMotionListener . The technique is quite similar. Refer to the Java documentation for the corresponding classes. To connect drawing routines to these events, you can use repaint() . However, it may be necessary to set up a graphics buffer and transfer the drawn graphic as a whole. We will cover these techniques later.

Key Events

Keyboard events are handled by the KeyListener interface. You need to implement three methods. Here's some example code.

import java.awt.Canvas;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

class TestCanvas extends Canvas implements KeyListener
{
    public TestCanvas ()
    {
        addKeyListener(this);
    }

    // KeyListener methods:

    public void keyPressed (KeyEvent e)
    {
        if (e.isActionKey())
            System.out.println("Action-Key : " + e.getKeyCode());
        else if (e.getKeyCode() == KeyEvent.VK_DELETE) // Bug?
            System.out.println("Action-Key : " + e.getKeyCode());
    }

    public void keyReleased (KeyEvent e)
    {
    }

    public void keyTyped (KeyEvent e)
    {
        System.out.println("Character : " + e.getKeyChar());
    }
}

As you can see, regular letter keys do not generate keyTyped() calls, and special keys do not. These are handled using keyPressed(). Due to a bug, the Delete key is not recognized as an action key.

The action key codes are all documented in KeyEvent. Examples are VK_F1, etc.

Andere Events

As another example of an event listener, we will look at WindowListener. This interface is used to react to events that affect the window's state. It implements a whole set of methods. The TestFrame class must implement all of these methods, even though only one is needed. This single method is windowClosing() and is called when the user clicks the close button in the window frame, or when the operating system wants to close the window.

import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import javax.swing.JFrame;

class Test extends JFrame
        implements WindowListener
{
    public Test ()
    {
        super("Test Frame");
        setSize(300, 300);
        addWindowListener(this);
    }

    // Methods of WindowListener:

    @Override
    public void windowActivated (WindowEvent e)
    {
    }

    @Override
    public void windowClosed (WindowEvent e)
    {
    }

    @Override
    public void windowClosing (WindowEvent e)
    {
        // Close the window and exit the program:
        dispose();
        System.exit(0);
    }

    @Override
    public void windowDeactivated (WindowEvent e)
    {
    }

    @Override
    public void windowDeiconified (WindowEvent e)
    {
    }

    @Override
    public void windowIconified (WindowEvent e)
    {
    }

    @Override
    public void windowOpened (WindowEvent e)
    {
    }

    public static void main (String args[])
    {
        JFrame F = new Test();
        F.setVisible(true);
    }
}

We've chosen a different method here to test our program. The window class is the test itself, and it also contains the main program, `main`. This is a good way to incorporate test routines into classes.

There are a number of other event listener interfaces, all of which listen for specific types of events (mouse events, keyboard input, etc.). Here is one.List.

Event Class

Interface

Methods

Sources

ActionEvent

ActionListener

actionPerformed()

Button
MenuItem
TextField

AdjustmentEvent

AdjustementListener

adjustmentValueChanged()

Scrollbar

ComponentEvent

ComponentListener

componentHidden()
componentMoved()
componentResized()
componentShown()

Component

ContainerEvent

ContainerListener

componentAdded()
componentRemoved()

Container

FocusEvent

FocusListener

focusGained()
focuseLost()

Component

ItemEvent

ItemListener

itemStateChanged()

Checkbox
CheckboxMenuItem
Choice
List

KeyEvent

KeyListener

keyPressed()
keyReleased()
keyTyped()

Component

MouseEvent

MouseListener

mouseClicked()
mouseEntered()
mouseExited()
mousePressed()
mouseReleased()

Component

 

MouseMotionListener

mouseDragged()
mouseMoved()

Component

TextEvent

TextListener

textValueChanged()

TextComponent

WindowEvent

WindowListener

windowActivated()
windowClosed()
windowClosing()
windowDeactivated()
windowDeiconified()
windowIconified()
windowOpened()

Window

Many of the event sources listed here are unfamiliar to us. However, note that Frame is derived from Window and can therefore also be a source of a WindowEvent . Similarly, all the components we've discussed so far (Canvas, Panel, etc.) are derived from Component. The same applies to the Swing classes prefixed with J, which can usually generate the same events.

Adapter Class

To prevent a class from becoming bloated with too many methods of an interface, there are adapter classes . These classes implement the interface with empty methods. You only need to override the corresponding method.

In the following example, we use the WindowAdapter, which implements the WindowListener interface. We simply override the windowClosing() method.

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;

class Closer extends WindowAdapter
{
    @Override
    public void windowClosing (WindowEvent e)
    {
        System.exit(0);
    }
}

class Test extends JFrame
{
    String Close = "Close";

    public Test ()
    {
        super("Test Frame");
        setSize(300, 300);
        addWindowListener(new Closer());
    }

    public static void main (String args[])
    {
        JFrame F = new Test();
        F.setVisible(true);
    }
}

This becomes even simpler if you declare Closer as an inner class . To do this, simply copy the declaration of Closer into the TestFrame class. The internal relationship will then become clear. Furthermore, you can use the variables of TestFrame.

class TestFrame extends Frame implements ActionListener
{   
    ...
    class Closer extends WindowAdapter
    {   
        public void windowClosing (WindowEvent e)
        {   
            dispose(); System.exit(0);
        }
    }
    ...
}

Everything else remains the same. Note that the inner class can access the variables of the enclosing class (in this case, Close).

The adapters for the other events are called MouseAdapter , KeyAdapter , etc.

Anonymous classes

But it can be even more concise. Instead of declaring a class at all, you use anonymous classes . The declaration is done directly within the `new` statement. This is a very compact way to declare child classes and create an instance immediately. It's clear that this class definition cannot be reused.

The instance of the anonymous class can access the variables of the class in which it was declared, just like a locally defined inner class.

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;

class Test extends JFrame
{
    String Close = "Close";

    public Test ()
    {
        super("Test Frame");
        setSize(300, 300);

        addWindowListener(
                new WindowAdapter()
                {
                    @Override
                    public void windowClosing (WindowEvent e)
                    {
                        dispose();
                        System.exit(0);
                    }
                });
    }

    public static void main (String args[])
    {
        JFrame F = new Test();
        F.setVisible(true);
    }
}

This example shows an anonymous inner class, namely a child of WindowAdapter. The syntax is

new VaterKlasse ( ConstructorParameter )
 Class definition

Exercisetasks

  1. Write a component called TestCanvas that reacts to mouse clicks. To do this, implement the MouseListener interface, which processes mouse clicks, and initiate the repaint process there (after first storing the click coordinates). A rectangle should be drawn at the clicked location. Alternatively, you can use the MouseAdapter. 
  2. Consult the documentation on keyboard events. Write a frame that can be closed with the Escape key. Do this using KeyAdapter .
  3. Now place a canvas in the window and check which component receives the keyboard event. What happens if you place two canvas components with a GridLayout in the window? How can you ensure that pressing the Escape key still closes the window?
  4. Now, in addition to the Canvas, add a TextField component to the window (for example, in the south with a BorderLayout). Does the Canvas still receive keyboard focus?
  5. Modify step 5 by making TestCanvas respond to mouse clicks. If someone clicks in TestCanvas, call requestFocus(). Does the canvas then receive mouse events?

Solutions .

Problems without solutions

  1. Write a window that has a canvas in the center and a panel with several buttons at the bottom. What happens if there isn't enough space for all the buttons?
  2. In step 1, use a panel with a GridLayout and place 5 buttons numbered 1 through 5 inside it. The canvas should always display the text of the last pressed button. To achieve this, simply register the canvas as an ActionListener for the buttons.
  3. Write a class called Test that opens two windows. Both windows should respond to windowClosing. However, the program should only terminate when the last frame has been closed.
  4. A more challenging task: Write a child class of Frame that has a method
    `void addCloseListener(CloseListener c);` . A CloseListener is an interface that you also need to write, and which implements a method `void frameClosed(Frame f);` . Now a CloseListener can register with your Frame. Call `frameClosed()` from the `windowClosed` method of `WindowListener`.


Back to the Java course