Capturing events on a button

Asked

Viewed 1,794 times

2

I’m starting in GUI in Java and wanted to understand how it works to capture events on a button, as for example in the code below:

Simplegui1.java

import javax.swing.*;
import java.awt.event.*;

public class SimpleGui1 implements ActionListener {

    JButton button;

    public static void main( String[] args ) {

        SimpleGui1 gui = new SimpleGui1();
        gui.go();

    } // fim do método main

    public void go() {

        JFrame frame = new JFrame();
        button = new JButton( "click me" );

        button.addActionListener( this );

        frame.getContentPane().add( button );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setSize( 300, 300 );
        frame.setVisible( true );

    } // fim do método go

    public void actionPerformed(ActionEvent event) { // Aqui está minha dúvida
                                                     // Em que momento é chamado o
        button.setText( "I've been clicked!" );      // método?

    } // fim do método actionPerformed

} // fim da classe SimpleGui1

How does this communication between the origin of the event (button) and the listener of the event?

Interface ActionListener in the documentation:

public abstract interface java.awt.event.ActionListener extends java.util.EventListener {

  // Method descriptor #8 (Ljava/awt/event/ActionEvent;)V
  public abstract void actionPerformed(java.awt.event.ActionEvent arg0);
}

Thank you!

1 answer

2


There is a good tutorial from Oracle on how to instantiate and register Listeners, deal with events, etc (link). From the conceptual point of view events, producers and listeners exist to separate the source of a stimulus and the points of application interested in dealing with it.

In short:

  1. The hierarchy of components that implement the functionality of JButton (including native components, which is not the case) receive from the Operating System a notification of click. There is a whole work going on at the JVM to know that that click happened within the limits of JButton, at a time when it is active and visible and for the user, as well as to determine whether the JButton really should answer for the event within the hierarchy of nested components.
  2. All JComponent owned a Eventlistenerlist containing classes interested in "listening" to certain types of events. When you call button.addActionListener, your ActionListener is registered on that list:

    public void addActionListener(ActionListener l) {
       listenerList.add(ActionListener.class, l);
    }
    
  3. An event (which inherits from java.util.EventObject; in case a Actionevent) is created containing the relevant information of the stimulus (the origin, type of event, when it occurred, etc, etc, etc). For the JButton this usually occurs or in the method setPressed in class Defaultbuttonmodel or in the method fireActionPerformed class AbstractButton (if you use a model lazy, something that again does not come to the point):

    ActionEvent e = null;
    // ...
    e = new ActionEvent(AbstractButton.this,
                        ActionEvent.ACTION_PERFORMED,
                        actionCommand,
                        event.getWhen(),
                        event.getModifiers());   
    

    The interesting thing about the current implementation is that AbstractButton registers to listen to events of the model, simply propagating the events generated by the model to the listeners registered with it (see internal class code Abractbutton.Handler to understand the functioning of that party).

  4. It is up to the component to execute the payloads of Listeners registered. In the case of JButton this also occurs in the method fireActionPerformed:

    protected void fireActionPerformed(ActionEvent event) {
         // Guaranteed to return a non-null array
         Object[] listeners = listenerList.getListenerList();
         ActionEvent e = null;
         // Process the listeners last to first, notifying
         // those that are interested in this event
         for (int i = listeners.length-2; i>=0; i-=2) {
             if (listeners[i]==ActionListener.class) {
                 // Lazily create the event:
                 if (e == null) {
                     String actionCommand = event.getActionCommand();
                     if(actionCommand == null) {
                         actionCommand = getActionCommand();
                     }
                     e = // Código do item 3
                 }
                 ((ActionListener)listeners[i+1]).actionPerformed(e);
            }
        }
    }
    
  5. Finally, at that moment the polymorphism goes into action. One of the ActionListeners registered to the JButton will be an instance of the class SimpleGui1 (implementing the interface ActionListener and registered with JButton in step 2). When the method actionPerformed is called in step 4, the payload that you created is invoked by changing the text of the button.

    public void actionPerformed(ActionEvent event) { 
        button.setText("I've been clicked!");    
    }
    

Therefore, to answer your question, the method actionPerformed runs on the line ((ActionListener)listeners[i+1]).actionPerformed(e); of the method fireActionPerformed class AbstractButton.

In case you’re wondering why none of these "obscure" details about how a ActionListener is invoked to be in the official tutorial of Sun Oracle, the reason is encapsulation. These are implementation details (which can change). What Apis users need to know is that payloads shall be written within the methods specified by Listeners, and that these Listeners must be registered with the components that produce the events consumed by the Listeners.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.