events - 自定义事件调度程序 - JavaFX
问题描述
有谁知道如何基于 javafx.event 包编写自定义事件调度程序?我在 Google & Co. 中进行了搜索,但没有找到一个很好的例子。
有没有人给我一个简约的例子?那会很好——我试了几次来理解它,但我失败了。
解决方案
首先要了解的是 JavaFX 如何分派事件。
当 anEvent
被触发时,它有一个关联的EventTarget
. 如果目标在场景图中,则路径从Event
开始Window
并沿着场景图向下直到EventTarget
到达。然后返回场景图,直到Event
再次到达。这分别称为“捕获阶段”和“冒泡阶段”。在捕获阶段调用事件过滤器,在冒泡阶段调用事件处理程序。使用属性(例如)设置的s是特殊类型的处理程序(即不是过滤器)。Window
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
的 s。您可能还想知道为什么这些方法返回一个Event
. 原因很简单:在这些方法的处理过程中,Event
连同tail.dispatchEvent
,可能会改变Event
. consume()
但是,除了 之外,Event
它的子类基本上是不可变的。这意味着任何其他更改都需要创建一个新的Event
. 事件处理过程的其余部分应该使用这个新的。 Event
调用tail.dispatchEvent
几乎总是会返回一个新的Event
. 这是因为 中的每个EventDispatcher
通常都EventDispatchChain
与自己的源相关联(例如 aLabel
或Window
)。当 anEventHandler
被调用时, 的来源Event
必须Object
与注册的相同EventHandler
;如果 anEventHandler
在 a 中注册,Window
则event.getSource()
必须Window
在 saidEventHandler
执行期间返回它。实现这一点的方法是使用该Event.copyFor(Object,EventTarget)
方法。
Event oldEvent = ...;
Event newEvent = oldEvent.copyFor(newSource, oldEvent.getTarget());
如您所见,EventTarget
通常始终保持不变。此外,子类可能会覆盖copyFor
,而其他的,例如MouseEvent
,也可能会定义重载。
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.
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
).
What do I mean by appropriate?
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
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.
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".
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.
推荐阅读
- html - 锚标签正在破坏轮播滑块 - Bootstrap 4
- c# - ASP.NET Core 2.x - HttpClient api returns error 'Object reference not set to an instance of an object'
- python - 如何使用 miniconda 获得一个完全干净的 python 环境?
- java - 像whatsapp这样的tablayout动画
- select - 如何将选项值存储在动态的依赖选项中?
- batch-file - 批量转到条件
- hibernate - 无法将值 NULL 插入到列“trackerId”、表“历史”中;列不允许空值。插入失败
- javascript - 替换多个字母保持正确的光标位置
- css - CSS 似乎无法正常工作
- java - 如何使用 k8s java 客户端连接到 pod 内的 k8s api 服务器