Capturing events on a button

2

I'm starting in Java GUI and wanted to understand how event trapping works on a button, such as 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 source of the event (button) and the listener of the event work?

Interface ActionListener in 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!

    
asked by anonymous 03.08.2014 / 06:40

1 answer

2

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

In short:

  • The hierarchy of components that implement the JButton functionality (including native components, which is not the case) receives a click notification from the Operating System. There is a lot of work going on in the JVM to know that that click occurred within the limits of JButton , at a time that it is active and visible and for the user, as well as to determine if JButton should actually respond for the event within the hierarchy of nested components.
  • All JComponent has a EventListenerList containing the classes interested in "listening" for certain types of events. When you call button.addActionListener , your ActionListener is registered in this list:

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

    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 itself registers itself to listen for model events by simply propagating the events generated by the model to the listeners registered with it (see the code of the inner class / a> to understand how this part works).

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

    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);
            }
        }
    }
    
  • Finally, at that point the polymorphism comes into play. One of the ActionListener s registered for JButton will be an instance of the SimpleGui1 class (which implements the ActionListener interface and registered with JButton in step 2). When the actionPerformed method is called in step 4, the payload you created is invoked by changing the button text.

    public void actionPerformed(ActionEvent event) { 
        button.setText("I've been clicked!");    
    }
    
  • So, to answer your question, the actionPerformed method runs on line ((ActionListener)listeners[i+1]).actionPerformed(e); of method fireActionPerformed of class AbstractButton .

    If you're wondering why none of these "obscure" details about how ActionListener is invoked are in Oracle's Sun official tutorial, the reason is encapsulation. These are implementation details (which may change). What API users need to know is that payloads should be written within the methods specified by Listeners , and that Listeners components that produce events consumed by Listeners .

        
    03.08.2014 / 21:41