Memra

Event handling & listeners

Respond to button clicks with ActionListener, attach listeners as anonymous inner classes (and lambdas), and drive the Greenhouse panel’s enable/disable button-state rules.

The listener (observer) model

Swing is event-driven: components do nothing on their own; they *fire events* and you register listeners that respond. A button click fires an ActionEvent; you handle it by registering an object that implements ActionListener:

public interface ActionListener {
    void actionPerformed(ActionEvent e);
}

ActionListener is a functional interface — a single abstract method — which is exactly why it pairs so naturally with anonymous inner classes (Module 2) and lambdas. You attach a listener with button.addActionListener(...). When the user clicks, Swing calls actionPerformed with an ActionEvent describing the source and the command.

Three ways to supply the listener

All three implement the same interface; they differ only in syntax and verbosity.

  1. A named class that implements ActionListener — verbose, but reusable and testable.
  2. An anonymous inner class — the classic pre-Java-8 idiom; defines the listener inline at the point of attachment. It can close over final/effectively-final locals and the enclosing instance’s fields.
  3. A lambda (Java 8+) — the shortest form, available because ActionListener has exactly one method. The exam (anchored to the 2006 textbook) emphasises the anonymous-inner-class form, so know both.

Reading WHICH button fired

When one listener serves several buttons, e.getActionCommand() returns the button’s text (or an explicitly set command string), and e.getSource() returns the component object itself. Compare against those to branch.

State drives enablement

The Greenhouse panel’s rule: while the controller is running, *Run* and *Open* are disabled and *Suspend* is enabled; while it is suspended, *Resume* is enabled and *Suspend* is disabled; while it is stopped, only *Run* and *Open* are enabled. You implement this by calling setEnabled(false) / setEnabled(true) on the relevant buttons whenever the controller’s state changes — a button-state machine.

Worked example — a self-disabling Run button

Here *Run* disables itself and enables *Suspend* the moment it is clicked, using an anonymous inner class so it can reach the enclosing fields runButton and suspendButton:

runButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        log.append("Controller started\n");
        runButton.setEnabled(false);    // can't run twice
        suspendButton.setEnabled(true); // now suspendable
    }
});

The identical handler as a lambda, since ActionListener is functional:

runButton.addActionListener(e -> {
    log.append("Controller started\n");
    runButton.setEnabled(false);
    suspendButton.setEnabled(true);
});

Trace it: clicking *Run* appends a line to the log text area, then flips the two buttons’ enabled flags. Swing repaints the buttons greyed/active automatically — you change state, the toolkit reflects it. Because the anonymous class is an inner class, runButton, suspendButton, and log resolve to the enclosing object’s fields with no extra plumbing; that direct access to enclosing state is the whole reason inner classes power GUI callbacks.

NORMAL ~/memra/learn/comp-308/event-handling-listeners utf-8 LF