有谁知道如何基于 javafx.event 包编写自定义事件调度程序?我在 Google & Co. 中搜索过,但没有找到一个很好的例子。
有没有人给我一个简约的例子?那会很好 - 我尝试了几次以理解它,但我失败了。
首先要意识到的是 JavaFX 是如何调度事件的。
当 anEvent
被触发时,它有一个关联的EventTarget
. 如果目标在场景图中,则路径从Event
开始Window
并沿着场景图向下直到EventTarget
到达。在Event
随后进入备份的场景图,直到它到达Window
一次。这分别称为“捕获阶段”和“冒泡阶段”。在捕获阶段调用事件过滤器,在冒泡阶段调用事件处理程序。EventHandler
使用onXXX
属性(例如onMouseClicked
)设置的s是特殊类型的处理程序(即不是过滤器)。
该EventDispatcher
接口有以下方法:
public Event dispatchEvent(Event event, EventDispatChain tail) { ... }
在这里,event
是Event
被调度的,tail
是EventDispatchChain
构建的,可能是递归的,由EventTarget.buildEventDispatchChain(EventDispatchChain)
。null
如果event
在方法执行期间消耗了 ,这将返回。
的EventDispatchChain
是一个堆栈的EventDispatcher
第 每次你打电话时,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
s。您可能还想知道为什么这些方法返回Event
. 原因很简单:在的处理Event
这些方法,伴随着tail.dispatchEvent
,可能会改变的Event
。consume()
然而,除了 之外,Event
它的子类基本上是不可变的。这意味着任何其他更改都需要创建一个新的Event
. 事件处理过程的其余部分应该使用这个新的 Event
。
对 的调用tail.dispatchEvent
实际上总是返回 的新实例Event
。这是由于每一个这样的事实EventDispatcher
在EventDispatchChain
通常与它自己的相关联的源(例如一个Label
或Window
)。当EventHandler
被调用的源Event
必须是相同的Object
,该EventHandler
所注册的; 如果 anEventHandler
是用 a 注册的,Window
那么在 said的执行过程中event.getSource()
必须返回那个。实现这一点的方法是使用该方法。Window
EventHandler
Event.copyFor(Object,EventTarget)
Event oldEvent = ...;
Event newEvent = oldEvent.copyFor(newSource, oldEvent.getTarget());
如您所见,EventTarget
通常始终保持不变。此外,子类可以覆盖,copyFor
而其他子类,例如MouseEvent
,也可以定义重载。
事件实际上是如何发送到EventHandler
s 的?好吧, 的内部实现EventDispatcher
使它们成为EventHandler
s的一种“集合” 。每个都EventDispatcher
跟踪onXXX
已添加到其关联源(例如Node
)或从中删除的所有过滤器、处理程序和属性处理程序()。你EventDispatcher
没有这样做,但它需要一种方法来访问无论你做存储EventHandler
秒。
在捕获阶段的EventDispatcher
调用所有适当 EventHandler
s可通过加入addEventFilter(EventType,EventHandler)
。然后,在冒泡阶段,EventDispatcher
调用通过或(例如)添加的所有适当的 EventHandler
s 。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
在调度 an 时,Event
您必须调用EventHandler
为Event
'sEventType
和所有EventType
's 超类型注册的所有s。另外,还要注意,食用的Event
并没有停止在处理Event
的当前阶段的的电流EventDispatcher
,而是完成调用一切适当EventHandler
秒。一旦该相位即 EventDispatcher
已经完成,然而,的处理Event
停止。
无论您使用何种机制来存储EventHandler
s,都必须能够由同一线程进行并发修改。这是因为对于相同的阶段,一个人EventHandler
可能会EventHandler
在相同的来源中添加或删除另一个EventType
。如果您将它们存储在常规中,List
这意味着List
在您迭代时可能会对其进行修改。EventHandler
可以自行删除的 的现成示例是WeakEventHandler
。WeakEventHandler
如果在“垃圾收集”之后调用它,A将尝试删除自身。
另外,我不知道这是否是必需的,但内部实现不允许EventHandler
为相同的源EventType
、 和阶段多次注册相同的内容。但是请记住,EventHandler
添加的 viaaddEventHandler
和添加的 viasetOnXXX
是分开处理的,即使它们都在同一阶段(冒泡)期间被调用。此外,调用会setOnXXX
替换EventHandler
同一属性的任何先前设置。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句