【问题标题】:EventListenerList firing orderEventListenerList 触发顺序
【发布时间】:2011-01-10 17:10:03
【问题描述】:

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

附录:EventListenerList 中的讨论似乎是实施建议而不是保证。 pstanton 建议的链接方法更可靠地执行正确的顺序。例如,SubPanelChangeListener 可以简单地将事件转发给父级。

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

【问题讨论】:

  • 有趣的问题和讨论! 1+
  • Converter 应用程序在ConverterRangeModel 中包含一个示例。
  • 我刚刚收到你的帖子,而googling,想详细解释一下。如果我描述的任何内容有误,请指出我的帖子!

标签: java swing event-listener


【解决方案1】:

由于 JSlider 和 JComponent 等文档没有提到监听器通知的顺序,我会犹豫是否依赖它,至少没有对 JRE 的每个后续版本进行彻底测试。

如果确实需要依赖顺序,可以考虑设置一个监听器链,即监听器一会通知监听器二等。

【讨论】:

  • 通常在状态变化(相对于输入)事件上,完全忽略事件对象更为稳健。 (不要对性能感到困惑 - 考虑一下您使用的周期数与平均 CPU 在一毫秒内可以执行的 数百万 个周期相比)。
  • @Tom:这是对我的回答还是他的问题的评论?
【解决方案2】:

有点老,很晚才回答。但是我不稳定的头脑真的迫使我偷偷摸摸。

我可以依赖该订单还是应该安排发送不同的订单 活动?

我相信他们维持秩序,组件的文档并没有告诉我们太多,但源代码始终是我们的朋友。让我们从JSlideraddChangeListener(listener)函数开始:

步骤 1: 调用 jSlider.addChangeListener(listener) 会将 listener 添加到 listener list

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

STEP 2: EvenListenerList 的源代码:synchronized add(Class<T> t, T l):添加监听器和相应的类型,以便在 Object[] 的末尾添加新的监听器,并为索引 @987654330 @,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 步:JSliderfireStateChanged() 函数负责向列表中的每个侦听器发送事件。源代码告诉我们,它通过从侦听器列表的末尾访问它们来调用每个侦听器的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);
            }
        }
    } 

总结:监听器列表中监听器的(同步)添加和访问机制告诉我们:它保持了LAST ADD FIRST VISIT的顺序。也就是说,先调用后(子)添加的侦听器,然后调用前(父)添加的侦听器,依此类推。 Swing 事件处理代码在 EDT 上运行。由于EventQueue 以与enqueued 相同的顺序调度事件,因此子事件将在父事件之前调度。

所以我确实相信 订单 得到了维护。

【讨论】:

  • 虽然是一个有趣的设计模式,但它是一个我不愿依赖的实现细节。
  • 可能,但对我来说,调用EventListListener机制不可靠似乎我们在责骂它:);)
  • 只是为了强调@trashgod 的评论:通知的顺序是明确未指定的(参见 f.i. beans 规范)。此外,在可观察对象 f.i. 的生命周期中,顺序可能会发生变化。通过删除/添加 LAF 更改的侦听器。所以,作为一般规则不要依赖任何具体的实现,它可能会在没有通知的情况下发生变化!
  • 好的,我会研究一下规范。我需要被说服
猜你喜欢
  • 1970-01-01
  • 2019-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-09
  • 1970-01-01
  • 1970-01-01
  • 2010-09-10
相关资源
最近更新 更多