问题描述
有人知道如何基于javafx.event包编写自定义事件分派器吗?我在Google&公司,但找不到一个很好的例子.
Does anyone know how to write a custom Event Dispatcher based on the javafx.event package? I searched in Google & Co. but didn't find a nice example.
有人对我有一个简约的例子吗?那就太好了-我尝试了几次以了解它,但失败了.
Have anyone a minimalistic example for me? That would be nice - I tried it a few times to understand it, but I failed.
推荐答案
首先要意识到的是JavaFX如何调度事件.
The first thing to realize is how JavaFX dispatches events.
当触发Event
时,它具有关联的EventTarget
.如果目标在场景图中,则Event
的路径从Window
开始并沿着场景图下降,直到到达EventTarget
.然后Event
将场景图备份,直到它再次到达Window
.这分别称为捕获阶段"和冒泡阶段".事件 filters 在捕获阶段被调用,事件 handlers 在冒泡阶段被调用.使用onXXX
属性(例如onMouseClicked
)设置的EventHandler
是处理程序的特殊类型(即不是过滤器).
When an Event
is fired it has an associated EventTarget
. If the target was in the scene-graph then the path of the Event
starts at the Window
and goes down the scene-graph until the EventTarget
is reached. The Event
then goes back up the scene-graph until it reaches the Window
again. This is known as the "capturing phase" and the "bubbling phase", respectively. Event filters are invoked during the capturing phase and event handlers are invoked during the bubbling phase. The EventHandler
s set using the onXXX
properties (e.g. onMouseClicked
) are special types of handlers (i.e. not filters).
EventDispatcher
界面具有以下方法:
public Event dispatchEvent(Event event, EventDispatChain tail) { ... }
在这里,event
是正在分发的Event
,而tail
是EventTarget.buildEventDispatchChain(EventDispatchChain)
可能递归构建的EventDispatchChain
.如果在执行该方法期间消耗了event
,则将返回null
.
Here, the event
is the Event
being dispatched and the tail
is the EventDispatchChain
built, possibly recursively, by EventTarget.buildEventDispatchChain(EventDispatchChain)
. This will return null
if the event
is consumed during execution of the method.
EventDispatchChain
是EventDispatcher
s的 stack .每次调用tail.dispatchEvent(event)
时,实际上是从顶部弹出EventDispatcher
并调用它.
The EventDispatchChain
is a stack of EventDispatcher
s. Every time you call tail.dispatchEvent(event)
you are essentially popping an EventDispatcher
off the top and invoking it.
@Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
// First, dispatch event for the capturing phase
event = dispatchCapturingEvent(event);
if (event.isConsumed()) {
// One of the EventHandlers invoked in dispatchCapturingEvent
// consumed the event. Return null to indicate processing is complete
return null;
}
// Forward the event to the next EventDispatcher in the chain
// (i.e. on the stack). This will start the "capturing" on the
// next EventDispatcher. Returns null if event was consumed down
// the chain
event = tail.dispatchEvent(event);
// once we've reached this point the capturing phase has completed
if (event != null) {
// Not consumed from down the chain so we now handle the
// bubbling phase of the process
event = dispatchBubblingEvent(event);
if (event.isConsumed()) {
// One of the EventHandlers invoked in dispatchBubblingEvent
// consumed the event. Return null to indicate processing is complete
return null;
}
}
// return the event, or null if tail.dispatchEvent returned null
return event;
}
您可能想知道dispatchCapturingEvent
和dispatchBubblingEvent
的定义位置.这些方法将由您创建并调用相应的EventHandler
.您可能还想知道为什么这些方法返回Event
.原因很简单:在处理Event
的过程中,这些方法以及tail.dispatchEvent
可能会更改 Event
.但是,除了consume()
之外,Event
及其子类基本上是不可变的.这意味着任何其他更改都需要创建一个新的Event
.其余事件处理过程应使用此 new Event
.
You're probably wondering where dispatchCapturingEvent
and dispatchBubblingEvent
are defined. These methods would be created by you and would invoke the appropriate EventHandler
s. You might also be wondering why these methods return an Event
. The reason is simple: During the processing of the Event
these methods, along with tail.dispatchEvent
, might alter the Event
. Other than consume()
, however, Event
and its subclasses are basically immutable. This means any other alterations require the creation of a new Event
. It is this new Event
that should be used by the rest of the event-handling process.
对tail.dispatchEvent
的调用实际上会始终返回Event
的新实例.这是由于EventDispatchChain
中的每个EventDispatcher
通常都与其自己的 source (例如Label
或Window
)相关联.调用EventHandler
时,Event
的来源必须与注册EventHandler
的Object
相同.如果EventHandler
已向Window
注册,则event.getSource()
必须在执行该EventHandler
的过程中返回该Window
.实现方法是使用Event.copyFor(Object,EventTarget)
方法.
The call to tail.dispatchEvent
will virtually always return a new instance of the Event
. This is due to the fact each EventDispatcher
in the EventDispatchChain
is normally associated with its own source (e.g. a Label
or Window
). When an EventHandler
is being invoked the source of the Event
must be the same Object
that the EventHandler
was registered to; if an EventHandler
was registered with a Window
then event.getSource()
must return that Window
during said EventHandler
's execution. The way this is achieved is by using the Event.copyFor(Object,EventTarget)
method.
Event oldEvent = ...;
Event newEvent = oldEvent.copyFor(newSource, oldEvent.getTarget());
如您所见,EventTarget
通常在整个过程中保持不变.另外,子类可以覆盖copyFor
,而其他子类(例如MouseEvent
)也可以定义重载.
As you can see, the EventTarget
normally remains the same throughout. Also, subclasses may override copyFor
while others, such as MouseEvent
, may also define an overload.
但是实际上如何将事件调度到EventHandler
?好吧,EventDispatcher
的内部实现使它们成为EventHandler
的一种集合".每个EventDispatcher
都跟踪已添加到其关联源(例如Node
)或从其关联源中删除的所有过滤器,处理程序和属性处理程序(onXXX
).您的EventDispatcher
不必执行此操作,但是无论您在做存储EventHandler
的任何位置,都需要一种访问方法.
How are the events actually dispatched to the EventHandler
s though? Well, the internal implementation of EventDispatcher
makes them a sort of "collection" of EventHandler
s. Each EventDispatcher
tracks all filters, handlers, and property-handlers (onXXX
) that have been added to or removed from its associated source (e.g. Node
). Your EventDispatcher
doesn't have to do this but it will need a way to access wherever you do store the EventHandler
s.
在捕获阶段,EventDispatcher
调用通过addEventFilter(EventType,EventHandler)
添加的所有适当 EventHandler
.然后,在冒泡阶段,EventDispatcher
调用通过addEventHandler(EventType,EventHandler)
或 setOnXXX
(例如setOnMouseClicked
)添加的所有适当 EventHandler
.
During the capturing phase the EventDispatcher
invokes all the appropriate EventHandler
s added via addEventFilter(EventType,EventHandler)
. Then, during the bubbling phase, the EventDispatcher
invokes all the appropriate EventHandler
s added via addEventHandler(EventType,EventHandler)
or setOnXXX
(e.g. setOnMouseClicked
).
适当是什么意思?
每个被触发的Event
都有一个关联的EventType
.所述EventType
可以具有超级EventType
.例如,MouseEvent.MOUSE_ENTERED
的继承"树为:
Every fired Event
has an associated EventType
. Said EventType
may have a super EventType
. For instance, the "inheritance" tree of MouseEvent.MOUSE_ENTERED
is:
Event.ANY
InputEvent.ANY
MouseEvent.ANY
MouseEvent.MOUSE_ENTERED_TARGET
MouseEvent.MOUSE_ENTERED
分派Event
时,必须调用为Event
的EventType
和的所有超类型注册的所有EventHandler
.另外,请注意,消耗Event
不会 停止在 current EventDispatcher
的 current phase 中对该Event
的处理,但是而是完成调用所有适当的EventHandler
的操作.但是,一旦那个 EventDispatcher
的那个阶段完成,Event
的处理就会停止.
When dispatching an Event
you have to invoke all the EventHandler
s registered for the Event
's EventType
and all the EventType
's supertypes. Also, note that consuming an Event
does not stop processing of that Event
for the current phase of the current EventDispatcher
but instead finishes invoking all appropriate EventHandler
s. Once that phase for that EventDispatcher
has completed, however, the processing of the Event
stops.
无论您使用哪种机制存储EventHandler
的 ,都必须能够通过同一线程并发修改 .这是因为EventHandler
可能会为同一阶段的同一EventType
向同一来源添加或从同一来源中删除另一个EventHandler
.如果将它们存储在常规的List
中,则意味着List
可以在迭代时进行修改.可以删除本身的EventHandler
的一个容易获得的示例是WeakEventHandler
.如果WeakEventHandler
被垃圾收集"后调用,它将尝试删除自身.
Whatever mechanism you use to store the EventHandler
s must be capable of concurrent modification by the same thread. This is because an EventHandler
may add or remove another EventHandler
to or from the same source for the same EventType
for the same phase. If you stored them in a regular List
this means the List
may be modified while you're iterating it. A readily available example of an EventHandler
that may remove itself is WeakEventHandler
. A WeakEventHandler
will attempt to remove itself if it is invoked after it has been "garbage collected".
此外,我不知道是否需要这样做,但是内部实现不允许为同一源,EventType
和阶段多次注册同一EventHandler
.但是,请记住,即使在同一阶段(冒泡)都调用了通过addEventHandler
添加的EventHandler
和通过setOnXXX
添加的EventHandler
.另外,调用setOnXXX
会替换同一属性的任何先前的EventHandler
设置.
Also, I don't know if this is required, but the internal implementation doesn't allow the same EventHandler
to be registered more than once for the same source, EventType
, and phase. Remember, though, that the EventHandler
s added via addEventHandler
and those added via setOnXXX
are handled separately even though they are both invoked during the same phase (bubbling). Also, calling setOnXXX
replaces any previous EventHandler
set for the same property.
这篇关于自定义事件分派器-JavaFX的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!