Devolus
Devolus

Reputation: 22104

How to properly add an ActionListeren to a custom JComponent

I often implement some panels, which provide common functionality like controls. For this I want to be able to add listeners, so that the caller can attach to the control and get notifications about changes.

So far I have simply used my own List where I keep the listeners and when I want to fire an action I loop through the list and call the listeners. From the outside this basically looks like any other Swing controls, however I was wondering if this is really the approach which should be used.

Especcially I was wondering if calling the listeners in a loop is what Swing itself also does, or if there is some kind of queue where you would put the actions, so that Swing decides when to deliver such actions.

When I investiaged this I came across this code:

protected void fireActionPerformed(ActionEvent event)
{
    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] instanceof ActionListener)
        {
            // Lazily create the event:
            if (e == null)
            {
                String actionCommand = event.getActionCommand();

                e = new ActionEvent(this,
                        ActionEvent.ACTION_PERFORMED,
                        actionCommand,
                        event.getWhen(),
                        event.getModifiers());
                e.setSource(event.getSource());
            }
            ((ActionListener)listeners[i+1]).actionPerformed(e);
        }
    }
}

The member listenerList from JComponent is directly accessed, whcih feels a bit strange. However I didn't really find a better way so far. Also when adding a new listener to this, I do it now like shown below, but I'm not sure if this is really the appropriate way:

public void addQueryListener(ActionListener oListener)
{
    listenerList.add(ActionListener.class, oListener);
}

public void removeQueryListener(ActionListener oListener)
{
    listenerList.remove(ActionListener.class, oListener);
}

So I would like to know, is accessing the listenerList member the correct way to add and remove listeners, so that they behave like any other standard control? Or is there some best practice how this should be done, which I'm missing so far?

Upvotes: 2

Views: 750

Answers (2)

Catalina Island
Catalina Island

Reputation: 7126

There's a good example in the EventListenerList docs, and this Converter example uses its own listenerList in ConverterRangeModel.

Upvotes: 1

veritas
veritas

Reputation: 2444

Keeping mind the restriction Swings put for Creating the gui. There is no harm accessing the ** listenerlist ** this way. May be this is not best approach. Swing is Suppose to be Single Threaded and is not Thread Safe.

http://codeidol.com/java/java-concurrency/GUI-Applications/Why-are-GUIs-Single-threaded/

AddListener and RemoveListener needed to be called on EDT(Event Dispatch Thread) see See http://en.wikipedia.org/wiki/Event_dispatching_thread.

Also See for iteration of Listenere i.e when you call getActionListeners

its creates a copy of ListenersList and returns you back

Below Code from EventListenerList

public <T extends EventListener> T[] getListeners(Class<T> t) {
Object[] lList = listenerList; 
int n = getListenerCount(lList, t); 
    T[] result = (T[])Array.newInstance(t, n); 
int j = 0; 
for (int i = lList.length-2; i>=0; i-=2) {
    if (lList[i] == t) {
    result[j++] = (T)lList[i+1];
    }
}
return result;   
}

Upvotes: 1

Related Questions