swing 事件处理程序是否应该在 EDT 上的事件之后排队?
swing 事件处理代码是否应该在 EDT 上的事件之后排队?如果是这样,是事件源负责调度事件处理程序,还是事件处理程序负责稍后调度实际的处理代码?
考虑:
JButton b = new JButton(); b.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ /* * SNIP 1 * do something that may affect other * components or generate new events... */ // SNIP 2 EventQueue.invokeLater(new Runnable(){ public void run(){ /* * do something that may affect other * components or generate new events... */ } }); } });
SNIP 1
在收到事件时运行处理程序代码。如果 JButton
负责通过 EventQueue.invokeLater()
调度事件,那么这可能是正确的。 SNIP 2
接收事件,并负责在接收到事件后调度处理程序代码(并由该特定事件类型的所有其他侦听器处理)。哪个是正确的?
编辑:为了清楚起见,我想稍后在 EDT 上安排事件处理程序,因为更改第一个事件处理程序中的状态可能会隐藏该组件的其他事件处理程序在事件发生时的原始状态。
Should swing event handling code be queued after the event on the EDT? If so, is it the responsibility of the event source to schedule the event handlers, or is it the responsibility of the event handler to schedule the actual handling code later?
Consider:
JButton b = new JButton(); b.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){ /* * SNIP 1 * do something that may affect other * components or generate new events... */ // SNIP 2 EventQueue.invokeLater(new Runnable(){ public void run(){ /* * do something that may affect other * components or generate new events... */ } }); } });
SNIP 1
runs handler code when the event is received. If the JButton
takes responsibility for scheduling event by EventQueue.invokeLater()
, then this is probably correct. SNIP 2
receives the event, and takes responsibility of scheduling handler code after the event is received (and handled by all other listeners for this specific event type). Which is correct?
EDIT: Just for clarity, I want to schedule event handlers later on EDT because changing state in the first event handler may hide original state at the time of the event from other event handlers for the component.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果我误解了您的问题,请告诉我,但 Swing 事件侦听器中的所有代码都是在 EDT 上运行的,因此无需将任何内容排队到 EDT。我很少看到 Swing 侦听器中使用的代码,例如 snip2 中的代码,通常是侦听器在后台线程中调用匿名内部 Runnable 的情况。
Please let me know though if I am misunderstanding your question, but all code in the Swing event Listeners is by run on the EDT, so there is no need to queue anything on to the EDT. I have only rarely seen code such as in snip2 used in a Swing listener, and usually that's if the listener calls an anonymous inner Runnable in a background thread.
正如 @HFOE 所指出的,
EventQueue 中的元素
是顺序和有序;不需要SNIP 2
。如果我理解您的要求,一种方法可能是将事件转发给任何其他感兴趣的侦听器,如问题&回答。As @HFOE notes, elements in the
EventQueue
are sequential and ordered;SNIP 2
shouldn't be required. If I understand your requirement, one approach might be to forward the event to any other interested listener(s), as suggested in this question & answer.我知道回答我自己的问题可能不太好,但我得出的结论是,应该在 Swing 中事件处理程序之后对事件处理程序进行排队。原因是这样的:每个组件可以有多个给定类型的侦听器。你不知道监听器在任何给定的组件上监听什么(swing本身可能会添加你不知道的监听器),也不知道它们对组件树做出什么假设,以及它们处理时树的状态一个事件。
现在,因为每个组件可以有针对特定事件类型的多个事件处理程序,所以我们不知道将首先调用哪个事件处理程序。如果我的事件处理程序修改了组件树,则同一事件类型的后续事件处理程序将看不到事件发生时的组件树。相反,它会看到我更改后的组件树。对于其他应用程序状态也存在同样的问题,这些状态可能由同一组件上相同类型的两个单独的侦听器进行检查和修改。这肯定是不对的。
其次,存在由 EDT 按顺序分派事件的概念。 IE。如果事件 x 实时发生在事件 y 之前,则 x 的事件处理程序应在 y< 的事件处理程序之前运行/em>。现在,如果我的组件(我们称之为c1),有 2 个用于事件
actionPerformed()
的事件处理程序 - eh1 和 eh2。当在 c1 上触发actionPerformed()
时,将调用 eh1 来处理该事件。然后,它修改组件c2,并导致在c2上触发itemStateChanged()
事件。因为此更改在第一个actionPerformed()
事件完成后(稍后在 EDT)排队,并且因为 c2 没有对itemStateChanged()
进行排队> 事件稍后在 EDT 上运行,itemStateChanged()
事件由其所有侦听器处理。只有这样,第二个actionPerformed()
侦听器 (eh2) 才会处理原始actionPerformed()
事件。如果 eh2 也是组件 c2 上
itemStateChanged()
事件的侦听器,那么 eh2 会认为itemStateChanged()
事件实际上发生在actionPerformed()
事件之前。这也肯定是不对的。这样就回答了问题 1。是的,修改应用程序状态或组件树的事件处理代码应该安排在事件处理后执行。事件处理程序应检查事件发生时需要检查的任何状态,但稍后在 EDT 上通过调用 EventQueue.invokeLater() 来对事件做出反应(进行更改)。
我的问题的第二部分是关于谁的责任是确保在事件本身发生之后处理事件。事后看来,这是一个愚蠢的问题。如果事件处理程序在事件发生时立即执行,或者稍后由组件调用 EventQueue.invokeLater() 执行,则存在同样的问题。
顺便说一句,当我研究这个问题时,我发现并非所有 Swing 组件都统一执行此操作。例如,
JCheckBox
在事件发生时立即触发itemStateChanged()
事件,而JComponent
则触发componentResized()
(通过是超类java.awt.Component
)稍后在 EDT 上。因此,处理事件的最可靠方法似乎是首先检查您的状态以决定您将采取哪些操作,然后在 EDT 稍后执行这些操作。有什么想法吗?我很想听听其他人对此的见解。
I know its probably bad form to answer my own question, but I've come to the conclusion that one should queue event handlers after an event in Swing. The reason is this: each component can have more than one listener of a given type. You don't know what listeners are listening on any given component (swing itself may add listeners that you are unaware of), and you don't know what assumptions they make about the component tree, and the state of the tree when they handle an event.
Now, because each component can have multiple event handler for a certain event type, we don't know which event handler will be invoked first. If my event handler modifies the component tree, a subsequent event handler for the same event type will not see the component tree as it was at the time of the event. Instead, it will see the component tree as it was after my changes. The same problem exists for other application state which may be inspected and modified by two separate listeners of the same type on the same component. This is surely not right.
Secondly, there is a concept of events being dispatched by the EDT in a sequential order. ie. If event x happened before event y in real time, then the event handlers for x should run before the event handlers for y. Now, if my component, lets call it c1, has 2 event handlers for event
actionPerformed()
- eh1 and eh2. WhenactionPerformed()
is fired on c1, eh1 is invoked to handle the event. It then modifies component c2, and causes aitemStateChanged()
event to be fired on c2. Because this change queued after the firstactionPerformed()
event was complete (later on the EDT), and because c2 does not queue theitemStateChanged()
event to run later on the EDT, theitemStateChanged()
event is handled by all its listeners. Only then does the secondactionPerformed()
listener (eh2) get to handle the originalactionPerformed()
event.If eh2 was also a listener to
itemStateChanged()
events on component c2, then it would appear to eh2 that theitemStateChanged()
event actually happened before theactionPerformed()
event. This too is surely not right.So that answers question 1. Yes, event handling code, which modifies application state or the component tree should be scheduled for execution after the event is processed. The event handlers should inspect whatever state they need to inspect at the time of the event, but react to the event (make changes) later on the EDT, by calling
EventQueue.invokeLater()
.The second part of my question is about whose responsibility it is to ensure that handling of events happens after the event itself has taken place. In hindsight, this is a stupid question. The same problem exists if the event handlers are executed immediately when an event occurs, or if they are executed later by the component calling
EventQueue.invokeLater()
.Incidentally, while I was researching this, I saw that not all Swing component do this uniformly. For example,
JCheckBox
fires theitemStateChanged()
events immediately when the event occurs, whileJComponent
firescomponentResized()
(via is super classjava.awt.Component
) later on the EDT.So it seems that the most robust way to handle events is to first inspect your state to make decisions about what actions you will take, then perform those actions later on the EDT. Any thoughts? I'd love to hear other people's insight into this.