EventListenerList порядок запуска
В приложении Swing у меня есть несколько подпанелей, каждая из которых слушает один JSlider
, Окружающая родительская панель также прослушивает все вложенные панели. Чтобы получить согласованные результаты в приведенном ниже примере, мне пришлось сначала добавить родителя, а затем локального слушателя. Это имеет смысл, учитывая порядок, предписанный в EventListenerList
и объяснил в этой статье. Могу ли я рассчитывать на этот заказ или мне следует организовать другое мероприятие?
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);
}
...
}
Приложение: обсуждение в EventListenerList
скорее совет по внедрению, чем гарантия. Подход цепочки, предложенный pstanton, обеспечивает более надежное выполнение правильного порядка. Например, SubPanel
"s ChangeListener
можно просто переслать событие родителю.
@Override
public void stateChanged(ChangeEvent e) {
...
parent.stateChanged(e);
}
2 ответа
Поскольку в документации для JSlider, JComponent и т. Д. Не указан порядок уведомления слушателя, я бы не стал полагаться на него, по крайней мере, без тщательного тестирования каждой последующей версии JRE.
Если вам действительно нужно положиться на порядок, рассмотрите возможность создания цепочки слушателей, т.е. слушатель один уведомит слушателя два и т. Д.
Немного стар и очень поздно, чтобы ответить. Но мой неустойчивый ум действительно заставляет меня суетиться.
Могу ли я рассчитывать на этот заказ или мне следует организовать другое мероприятие?
Я верю, что они поддерживают порядок, документация Компонента нам мало что говорит, но исходный код всегда наш друг. Давайте начнем с addChangeListener(listener)
функция JSlider
:
ШАГ 1: вызов jSlider.addChangeListener(listener)
добавляет listener
к listener list
,
public void addChangeListener(ChangeListener l) {
listenerList.add(ChangeListener.class, l);
}
ШАГ 2: исходный код EvenListenerList
: synchronized add(Class<T> t, T l)
: добавляет слушателей и соответствующий тип, так что новый слушатель добавляется в конце Object[]
и для индекса i
,Object[i]
это тип слушателя и Object[i+1]
это экземпляр слушателя.
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;
}
}
ШАГ 3: fireStateChanged()
функция JSlider
отвечает за отправку события каждому слушателю списка. Исходный код говорит нам, что он вызывает каждого слушателя stateChanged()
функция, посетив их из конца списка слушателей.
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: Механизм (синхронизированного) добавления и посещения слушателей в списке слушателей говорит нам, что: он поддерживает порядок LAST ADD FIRST VISIT. То есть сначала будет вызван добавленный позднее (дочерний) слушатель, затем предыдущий (родительский) добавленный слушатель и так далее. Код обработки событий Swing работает на EDT. И в качестве EventQueue
отправляет событие в том же порядке, в каком они есть enqueued
, дочернее событие будет отправлено до родительского события.
Поэтому я верю, что порядок поддерживается.