有人知道如何基于javafx.event包编写自定义事件分派器吗?我在Google&Co.中进行了搜索,但没有找到一个很好的示例。
有人对我有一个简约的例子吗?那就太好了-我尝试了几次以了解它,但失败了。
答案 0 :(得分:0)
首先要意识到的是JavaFX如何调度事件。
触发Event
时,它具有关联的EventTarget
。如果目标在场景图中,则Event
的路径从Window
开始,然后沿着场景图下降,直到到达EventTarget
。 Event
然后将场景图备份,直到它再次到达Window
。这分别称为“捕获阶段”和“冒泡阶段”。在捕获阶段调用事件 filters ,在冒泡阶段调用事件 handler 。使用EventHandler
属性(例如onXXX
)设置的onMouseClicked
是处理程序的特殊类型(即不是过滤器)。
EventDispatcher
界面具有以下方法:
public Event dispatchEvent(Event event, EventDispatChain tail) { ... }
在这里,event
是正在分发的Event
,而tail
是EventDispatchChain
可能通过递归方式构建的EventTarget.buildEventDispatchChain(EventDispatchChain)
。如果在执行该方法期间消耗了null
,它将返回event
。
EventDispatchChain
是EventDispatcher
s的堆栈。每次您致电tail.dispatchEvent(event)
时,实际上都是从顶部弹出EventDispatcher
并调用它。
@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
可能会{em>更改 Event
。但是,除consume()
以外,Event
及其子类基本上是不可变的。这意味着任何其他更改都需要创建新的Event
。此 new Event
应该在其余的事件处理过程中使用。
对tail.dispatchEvent
的调用实际上会始终返回Event
的新实例。这是由于EventDispatcher
中的每个EventDispatchChain
通常都与其自己的 source (例如Label
或Window
)相关联。调用EventHandler
时,Event
的来源必须与注册Object
的{{1}}相同。如果EventHandler
已向EventHandler
注册,则Window
必须在执行event.getSource()
的过程中返回Window
。实现方法是使用EventHandler
方法。
Event.copyFor(Object,EventTarget)
如您所见,Event oldEvent = ...;
Event newEvent = oldEvent.copyFor(newSource, oldEvent.getTarget());
通常在整个过程中保持不变。同样,子类可以覆盖EventTarget
,而其他子类(例如copyFor
)也可以定义重载。
这些事件实际上如何分配到MouseEvent
?好吧,EventHandler
的内部实现使它们成为EventDispatcher
的一种“集合”。每个EventHandler
跟踪已添加到其关联源(例如EventDispatcher
)或从其关联源中删除的所有过滤器,处理程序和属性处理程序(onXXX
)。您的Node
不必这样做,但是无论您在何处存储EventDispatcher
的地方,都需要一种访问方法。
在捕获阶段,EventHandler
调用通过EventDispatcher
添加的所有适当的 EventHandler
。然后,在冒泡阶段,addEventFilter(EventType,EventHandler)
调用通过EventDispatcher
或 EventHandler
添加的所有适当 addEventHandler(EventType,EventHandler)
(例如setOnXXX
)。
适当是什么意思?
每个被解雇的setOnMouseClicked
都有一个关联的Event
。所说的EventType
可以具有超级EventType
。例如,EventType
的“继承”树为:
MouseEvent.MOUSE_ENTERED
调度Event.ANY
InputEvent.ANY
MouseEvent.ANY
MouseEvent.MOUSE_ENTERED_TARGET
MouseEvent.MOUSE_ENTERED
时,您必须调用为Event
的{{1}} 和所有{{ 1}}的超类型。另外,请注意,消耗EventHandler
不会在当前{{1}的当前阶段中停止Event
的处理。 } ,但相反会完成调用所有适当的EventType
的操作。但是,一旦{em {em}那 {em}那一个{em}那个阶段,EventType
的处理就停止了。
无论您使用哪种机制存储Event
的 ,都必须能够通过同一线程并发修改 。这是因为对于同一阶段的同一Event
,EventDispatcher
可能会向同一源添加或从同一源中删除另一个EventHandler
。如果将它们存储在常规EventDispatcher
中,则意味着Event
可能会在迭代时被修改。可以删除本身的EventHandler
的一个容易获得的示例是EventHandler
。如果EventHandler
被“垃圾收集”后调用,它将尝试删除自身。
此外,我不知道是否需要这样做,但是内部实现不允许为同一源EventType
和阶段多次注册同一List
。但是,请记住,即使在同一阶段(冒泡)都调用了通过List
添加的EventHandler
和通过WeakEventHandler
添加的WeakEventHandler
。另外,调用EventHandler
会替换先前为同一属性设置的EventType
。