代码之家  ›  专栏  ›  技术社区  ›  trashgod

事件侦听器列表触发顺序

  •  8
  • trashgod  · 技术社区  · 15 年前

    在Swing应用程序中,我有许多子面板,每个子面板听一个 JSlider .周围的父面板还监听所有子面板。为了在下面的示例中获得一致的结果,我必须先添加父级,然后 然后 本地侦听器。这是有道理的,考虑到 EventListenerList 在这里解释 article . 我可以依赖那个订单吗,或者我应该安排发送一个不同的事件?

    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);
        }
        ...
    }
    

    附录:讨论 事件侦听器列表 似乎是执行建议而不是保证。链接方法由 pstanton 更可靠地执行正确的命令。例如, SubPanel ChangeListener 可以简单地将事件转发给父级。

        @Override
        public void stateChanged(ChangeEvent e) {
            ...
            parent.stateChanged(e);
        }
    
    2 回复  |  直到 11 年前
        1
  •  4
  •   pstanton    15 年前

    因为jslider和jcomponent等的文档没有提到侦听器通知的顺序,所以我会犹豫是否依赖它,至少在没有对JRE的每个后续版本进行彻底测试的情况下。

    如果你真的需要依赖这个顺序,可以考虑建立一个侦听器链,即listener one将通知listener two等。

        2
  •  1
  •   Sage    11 年前

    有点老,回答得很晚。但我不稳定的头脑真的迫使我窃笑。

    我能依赖那个订单吗?还是我应该安排另发一份 事件?

    我相信他们维护了订单,组件的文档并没有告诉我们很多,但是 源代码 永远是我们的朋友。让我们从 addChangeListener(listener) 功能 JSlider :

    步骤1: 打电话 jSlider.addChangeListener(listener) 添加 listener 到A 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() 功能 J滑块 负责向列表的每个侦听器发送事件。源代码告诉我们它调用每个侦听器的 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: 侦听器列表中侦听器的(同步)添加和访问机制告诉我们:它维护最后一次添加第一次访问的顺序。也就是说,后面添加的(子)侦听器将首先被调用,然后前面添加的(父)侦听器将被调用,依此类推。Swing事件处理代码在EDT上运行。以及 EventQueue 以相同的顺序发送事件 enqueued ,子事件将在父事件之前调度。

    所以我相信 秩序 正在维护。