自定义事件分派器-JavaFX [英] Custom Event Dispatcher - JavaFX

查看:112
本文介绍了自定义事件分派器-JavaFX的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人知道如何基于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 EventHandlers 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,而tailEventTarget.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.

EventDispatchChainEventDispatcher s的 stack .每次调用tail.dispatchEvent(event)时,实际上是从顶部弹出EventDispatcher并调用它.

The EventDispatchChain is a stack of EventDispatchers. 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;
}

您可能想知道dispatchCapturingEventdispatchBubblingEvent的定义位置.这些方法将由您创建并调用相应的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 EventHandlers. 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 (例如LabelWindow)相关联.调用EventHandler时,Event 的来源必须与注册EventHandlerObject相同.如果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 EventHandlers though? Well, the internal implementation of EventDispatcher makes them a sort of "collection" of EventHandlers. 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 EventHandlers.

在捕获阶段,EventDispatcher调用通过addEventFilter(EventType,EventHandler)添加的所有适当 EventHandler.然后,在冒泡阶段,EventDispatcher调用通过addEventHandler(EventType,EventHandler) setOnXXX(例如setOnMouseClicked)添加的所有适当 EventHandler.

During the capturing phase the EventDispatcher invokes all the appropriate EventHandlers added via addEventFilter(EventType,EventHandler). Then, during the bubbling phase, the EventDispatcher invokes all the appropriate EventHandlers 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时,必须调用为EventEventType 的所有超类型注册的所有EventHandler.另外,请注意,消耗Event不会 停止在 current EventDispatcher current phase 中对该Event的处理,但是而是完成调用所有适当的EventHandler的操作.但是,一旦那个 EventDispatcher那个阶段完成,Event的处理就会停止.

When dispatching an Event you have to invoke all the EventHandlers 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 EventHandlers. 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 EventHandlers 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 EventHandlers 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆