EventListenerList firing order

2019-01-01 01:19发布

In a Swing application, I have a number of sub-panels, each listening to a single JSlider. The surrounding parent panel also listens to all the sub-panels. To get consistent results in the example below, I had to add the parent first and then the local listener. This makes sense, given the order prescribed in EventListenerList and explained in this article. Can I rely on that order or should I arrange to send a different event?

class SubPanel extends JPanel implements ChangeListener {

    private final JSlider slider = new JSlider();
    private final JLabel label = new JLabel();
    private final String name;
    private float value;

    public SubPanel(String name, float value, ChangeListener parent) {
        this.name = name;
        this.value = value;
        ...
        slider.addChangeListener(parent);
        slider.addChangeListener(this);
    }
    ...
}

Addendum: the discussion in EventListenerList appears to be implementation advice rather than a guarantee. The chaining approach suggested by pstanton enforces the correct order more reliably. For example, the SubPanel's ChangeListener can simply forward the event to the parent.

    @Override
    public void stateChanged(ChangeEvent e) {
        ...
        parent.stateChanged(e);
    }

2条回答
大哥的爱人
2楼-- · 2019-01-01 01:35

Since the documentation for JSlider and JComponent etc don't mention the order of listener notification, I would hesitate to rely on it, at least without thorough testing on each subsequent version of the JRE.

If you really need to rely on the order, consider setting up a chain of listeners, ie Listener one will notify listener two etc.

查看更多
其实,你不懂
3楼-- · 2019-01-01 01:50

A bit old and very late to answer. But My unstable mind is really forcing me to snick into.

Can I rely on that order or should I arrange to send a different event?

I do believe that they maintain the order, the Component's documentation doesn't tell us much, but source code is always our friend. Let us start from addChangeListener(listener) function of JSlider:

STEP 1: calling jSlider.addChangeListener(listener) adds the listener to a listener list.

public void addChangeListener(ChangeListener l) {
        listenerList.add(ChangeListener.class, l);
    }

STEP 2: source code of EvenListenerList: synchronized add(Class<T> t, T l): adds the listeners and corresponding type such that new listener is added at the end of the Object[] and for an index i,Object[i] is type of the listener and Object[i+1] is the listener instance.

public synchronized <T extends EventListener> void add(Class<T> t, T l) {
    // There were other checking here
    // omitted as irrelevant to the discussion
    } else {
        // Otherwise copy the array and add the new listener
        int i = listenerList.length;
        Object[] tmp = new Object[i+2];
        System.arraycopy(listenerList, 0, tmp, 0, i);

        tmp[i] = t;  // add the class type
        tmp[i+1] = l;  // add the listener instance 

        listenerList = tmp;
    }
    }

STEP 3: The fireStateChanged() function of JSlider is responsible for sending event to every listener of the list. The source code tells us that it call each listener's stateChanged() function by visiting them from the end of the listener list.

protected void fireStateChanged() {
        Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i]==ChangeListener.class) {
                if (changeEvent == null) {
                    changeEvent = new ChangeEvent(this);
                }
                ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
            }
        }
    } 

Summery: The mechanism of (synchronized)adding and visiting of listeners in listeners list tells us that: it maintain the LAST ADD FIRST VISIT order. That is, the later(child) added listener is going to be called first, then the previous(parent) added listener and so on. Swing event handling code runs on EDT. And as EventQueue dispatches the event In the same order as they are enqueued, the child event will be dispatched prior to parent event.

So I do believe the order is being maintained.

查看更多
登录 后发表回答