自定义事件分派器-JavaFX

时间:2018-08-18 16:11:55

标签: events javafx

有人知道如何基于javafx.event包编写自定义事件分派器吗?我在Google&Co.中进行了搜索,但没有找到一个很好的示例。

有人对我有一个简约的例子吗?那就太好了-我尝试了几次以了解它,但失败了。

1 个答案:

答案 0 :(得分:0)

首先要意识到的是JavaFX如何调度事件。

触发Event时,它具有关联的EventTarget。如果目标在场景图中,则Event的路径从Window开始,然后沿着场景图下降,直到到达EventTargetEvent然后将场景图备份,直到它再次到达Window。这分别称为“捕获阶段”和“冒泡阶段”。在捕获阶段调用事件 filters ,在冒泡阶段调用事件 handler 。使用EventHandler属性(例如onXXX)设置的onMouseClicked处理程序的特殊类型(即不是过滤器)。

EventDispatcher界面具有以下方法:

public Event dispatchEvent(Event event, EventDispatChain tail) { ... }

在这里,event是正在分发的Event,而tailEventDispatchChain可能通过递归方式构建的EventTarget.buildEventDispatchChain(EventDispatchChain)。如果在执行该方法期间消耗了null,它将返回event

EventDispatchChainEventDispatcher 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;
}

您可能想知道dispatchCapturingEventdispatchBubblingEvent的定义位置。这些方法将由您创建并调用适当的EventHandler。您可能还想知道为什么这些方法返回Event。原因很简单:在处理Event期间,这些方法以及tail.dispatchEvent可能会{em>更改 Event。但是,除consume()以外,Event及其子类基本上是不可变的。这意味着任何其他更改都需要创建新的Event。此 new Event应该在其余的事件处理过程中使用。

tail.dispatchEvent的调用实际上会始终返回Event的新实例。这是由于EventDispatcher中的每个EventDispatchChain通常都与其自己的 source (例如LabelWindow)相关联。调用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 ,都必须能够通过同一线程并发修改 。这是因为对于同一阶段的同一EventEventDispatcher可能会向同一源添加或从同一源中删除另一个EventHandler。如果将它们存储在常规EventDispatcher中,则意味着Event可能会在迭代时被修改。可以删除本身EventHandler的一个容易获得的示例是EventHandler。如果EventHandler被“垃圾收集”后调用,它将尝试删除自身。

此外,我不知道是否需要这样做,但是内部实现不允许为同一源EventType和阶段多次注册同一List。但是,请记住,即使在同一阶段(冒泡)都调用了通过List添加的EventHandler和通过WeakEventHandler添加的WeakEventHandler。另外,调用EventHandler会替换先前为同一属性设置的EventType