JavaFX - EventDispatcher 和 EventFilter 之间的区别 [英] JavaFX - Difference between EventDispatcher and EventFilter

查看:28
本文介绍了JavaFX - EventDispatcher 和 EventFilter 之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解何时在 JavaFX 中使用 EventDispatcher.我非常了解捕获和冒泡的所有事件机制.但我仍然对 EventDispatcher 的目的感到困惑.因为我可以使用过滤器完成大部分工作处理程序.

I am trying to understand when to use EventDispatcher in JavaFX. I am pretty much aware of all the event mechanisms of capturing and bubbling. But I am still confused with the purpose of EventDispatcher. Because I can get most of my work done using filters & handlers.

谁能解释一下这个EventDispatcher的实际目的是什么以及如何使用?

Can someone please explain what is the actual purpose and how to use this EventDispatcher?

谢谢.

推荐答案

EventDispatcher

的目的

顾名思义,EventDispatcher 的目的是调度一个事件.它执行此操作的方式取决于实现.但是,那标准实现(JavaFX 内部)使 EventDispatcher 成为一种EventHandler 的集合".由 EventDispatcher 负责在正确的时间调用适当的 EventHandler.是什么使得一个 EventHandler 适当的"取决于:

Purpose of EventDispatcher

As the name suggests, the purpose of an EventDispatcher is to dispatch an Event. The way it does this is dependent on the implementation. However, the standard implementation (internal to JavaFX) makes EventDispatcher a kind of "collection" of EventHandlers. It is the responsibility of the EventDispatcher to invoke the appropraite EventHandler at the correct time. What makes an EventHandler "appropriate" depends on:

  • 如果 EventHandler 已为事件的当前阶段注册调度周期(捕获或冒泡)
  • 如果 EventHandler 是为当前 EventEventType 注册的,或者超类型之一(即 EventType.getSuperType())
  • If the EventHandler was registered for the current phase of the event dispatching cycle (capturing or bubbling)
  • If the EventHandler was registered for the current Event's EventType or one of the supertypes (i.e. EventType.getSuperType())

如果我们专注于场景图,当 Event 被触发时,它会从顶部开始的场景图(Window)并穿过层次结构到目标(通常是一个 Node,但至少是一个EventTarget).这是事件调度周期的捕获"阶段.后到达目标,它返回场景图,直到到达窗口 再次.这是周期的冒泡"阶段.

If we focus on the scene-graph, when an Event is fired it starts at the top of the scene-graph (the Window) and travels through the hierarchy to the target (usually a Node, but at the very least an implementation of EventTarget). This is the "capturing" phase of the event dispatch cycle. After reaching the target it travels back up the scene-graph until it reaches the Window again. This is the "bubbling" phase of the cycle.

  • Window -> Scene -> Root Node -> Middle Node -> EventTarget
  • Window -> Scene -> Root Node -> Middle Node -> EventTarget
  • Window <- Scene <- 根节点 <- 中间节点 <- <代码>事件目标
  • Window <- Scene <- Root Node <- Middle Node <- EventTarget

如果在任何步骤消耗了 Event(通过 Event.consume()),那么它不会转至下一步.这有效地停止了处理Event 在链中所需的步骤.

If at any step the Event is consumed (via Event.consume()) then it will not be forwarded to the next step. This effectively stops the processing of the Event at the desired step in the chain.

这条路径的计算方式是通过实现EventDispatchChainEventTarget.EventTarget 必须实现以下方法:

The way this path is computed is done by an implementation of EventDispatchChain and the EventTarget. An EventTarget must implement the following method:

EventDispatchChain buildEventDispatchChain(EventDispatchChain tail);

当一个 Event 被触发时,它有一个指定的 EventTarget.由于EventTarget 将位于链所在的场景图的底部自下而上建造.它通过将 EventDispatcher 添加到EventDispatchChain 在层次结构的每个级别(使用EventDispatchChain.prepend(EventDispatcher)).这是EventDispatcher开始进来了.

When an Event is fired it has a designated EventTarget. Since the EventTarget will be at the bottom of the scene-graph the chain is built from the bottom up. It does this by prepending an EventDispatcher to the EventDispatchChain at each level in the hierarchy (using EventDispatchChain.prepend(EventDispatcher)). This is where EventDispatcher starts to comes in.

每个 EventTarget 通常都有自己的 EventDispatcher 与之关联.这EventTarget 在标准 JavaFX 中的实现(WindowSceneNodeMenuItem 等...) 提供他们自己的 EventDispatcher 实现.在这方面你不必担心如何使用EventDispatcher.你不甚至直接使用它.相反,您通过添加 EventHandlers[add|remove]EventHandler[add|remove]EventFilter 方法以及各种 onXXX 属性.

Each EventTarget usually has its own EventDispatcher associated with it. The implementations of EventTarget in standard JavaFX (Window, Scene, Node, MenuItem, etc...) provide their own implementations of EventDispatcher. In this regard you don't have to worry about how to use EventDispatcher. You don't even use it directly. Rather, you add EventHandlers via the [add|remove]EventHandler and [add|remove]EventFilter methods as well as he various onXXX properties.

buildEventDispatchChain 被调用时,比如一个 ButtonButton 在前面它的 EventDispatcher 到给定的 EventDispatchChain.然后它调用buildEventDispatchChain 在它的 Parent 上,如果它有的话.这一直持续到Scene 的根 Node.根Node调用buildEventDispatchChain在上述 Scene 上,在它的 EventDispatcher 之前,在上面做同样的事情它附加到的 Window.

When buildEventDispatchChain is called on say, a Button, the Button prepends its EventDispatcher to the given EventDispatchChain. It then calls buildEventDispatchChain on its Parent if it has one. This continues up to the root Node of the Scene. The root Node calls buildEventDispatchChain on said Scene which, after prepending its EventDispatcher, does the same on the Window it is attached to.

此时 EventDispatchChain 已完全构建并准备好进行处理事件.如果还不明显,EventDispatchChain 基本上只是EventDispatcher 的堆栈".换句话说,它是一个高度专业化的java.util.Deque 但没有实际扩展该接口.

At this point the EventDispatchChain is fully built and is ready to process the Event. If not obvious yet, the EventDispatchChain is fundamentally just a "stack" of EventDispatchers. In other words, it is a highly specialized java.util.Deque but without actually extending that interface.

注意:EventDispatchChain 还提供了一个 append(EventDispatcher) 方法对于前置是错误操作的情况.

Note: EventDispatchChain also provides an append(EventDispatcher) method for the situations where prepending is the wrong operation.

一旦 EventDispatchChain 完全构建,就可以实际调度事件.这是通过在 EventDispatchChain 上调用这个方法来完成的:

Once the EventDispatchChain is fully built it is time to actually dispatch the Event. This is done by calling this method on the EventDispatchChain:

Event dispatchEvent(Event event);

这有 EventDispatchChain 获取(弹出)第一个 EventDispatcher堆栈并调用它的方法:

This has the EventDispatchChain get (pop) the first EventDispatcher on the stack and call its method:

Event dispatchEvent(Event event, EventDispatchChain tail);

旁注:不幸的是,EventDispatcher 的方法与EventDispatchChain.dispatchEvent(Event) 可能会引起混淆.

Side Note: Unfortunately EventDispatcher's method has a similar signature to EventDispatchChain.dispatchEvent(Event) which may cause confusion.

旁注 #2:tail 在整个过程中将是相同的 EventDispatchChain整个过程.

Side Note #2: The tail will be the same EventDispatchChain throughout the entire process.

这是实际使用 EventDispatcher 的地方.这是算法由内部定义的每个 EventDispatcher 使用com.sun.javafx.event.BasicEventDispatcher 类(Java 10 源代码):

This is where the EventDispatchers are actually used. Here is the algorithm used by each EventDispatcher as defined by the internal com.sun.javafx.event.BasicEventDispatcher class (Java 10 source code):

@Override
public Event dispatchEvent(Event event, final EventDispatchChain tail) {
    event = dispatchCapturingEvent(event);
    if (event.isConsumed()) {
        return null;
    }
    event = tail.dispatchEvent(event);
    if (event != null) {
        event = dispatchBubblingEvent(event);
        if (event.isConsumed()) {
            return null;
        }
    }

    return event;
}

步骤是:

  1. 为捕获阶段调度Event
    • 这会调用作为过滤器
    • 添加的所有EventHandler
    • 在此处使用 Event 不会停止 Event 在这一步;但会阻止 Event 在后面的步骤中被处理
  1. Dispatch the Event for the capturing phase
    • This invokes all the EventHandlers that were added as a filter
    • Consuming an Event here will not stop the dispatching of the Event in this step; but will stop the Event from being processed in later steps
  • 如果返回的 Eventnull,则表示 Event 已被消耗链下游的某个地方,无需进行更多处理
  • 这会调用作为 handler 添加的所有 EventHandlers(其中包括通过 onXXX 属性添加的那些)
  • 与第 1 步一样,在此处使用 Event 不会停止处理这一步;但会阻止 Event 在后面的步骤中被处理
  • If the returned Event was null that means the Event was consumed somewhere down the chain and no more processing is to be done
  • This invokes all the EventHandlers that where added as a handler (which includes the ones added via the onXXX properties)
  • As with step #1, consuming an Event here does not stop the processing of this step; but will stop the Event from being processed in later steps
  • EventDispatchChain 一样,返回 null 意味着 Event 已经Event 的消耗和处理必须停止
  • As with EventDispatchChain, returning null means the Event has been consumed and processing of the Event must stop

这是为 EventDispatchChain 中的每个 EventDispatcher 完成的.电话to tail.dispatchEvent 基本上是一个递归"操作(没有它真正的"递归).实际上,此代码沿堆栈向下走(调用tail.dispatchEvent) 然后返回堆栈(当 tail.dispatchEvent返回).并且每个链中的链接"在递归"之前进行处理调用(捕获阶段)和递归"调用返回后(冒泡阶段).

This is done for each EventDispatcher in the EventDispatchChain. The call to tail.dispatchEvent is bascially a "recursive" operation (without it being "real" recursion). In effect this code walks down the stack (the calls to tail.dispatchEvent) and then walks back up the stack (when tail.dispatchEvent returns). And each "link in the chain" does processing before the "recursive" call (the capturing phase) and after the "recursive" call returns (the bubbling phase).

但是请注意,在每个步骤中,EventDispatcher 是实际调用每个适当的 EventHandler .这就是一个EventDispatcher 使用.

Notice here, though, that at each step it is the EventDispatcher that is actually invoking each of the appropriate EventHandlers. This is how an EventDispatcher is used.

当扩展一个已经实现了 EventTarget 的类时,你应该只在绝对需要时创建您自己的EventDispatcher.如果你的目标是控制 Event 是否达到某个 EventTarget 那么你的第一选择应该是在适当的地方消费 Event(Jai 在评论).如果你想改变 Event 的路径,那么你可能需要提供您自己的 EventDispatcher.但由于封闭内部 EventDispatcher 实现的性质,加上事实EventDispatcher 接口是有限的,你可能会受到限制将原始 EventDispatcher 包装在您自己的实现中并委托必要时.我已经在其他人的代码中看到过这样做(甚至可能已经看到它在 JavaFX 本身中)但我记不起代码了,无法给你这方面的例子.

When extending a class that already implements EventTarget then you should only create your own EventDispatcher when absolutely needed. If your goal is to control if an Event reaches a certain EventTarget then your first choice should be to consume the Event at the appropriate place (mentioned by Jai in the comments). If you want to alter something about the path of the Event then you might need to provide your own EventDispatcher. However, due to the closed nature of the internal EventDispatcher implementations, coupled with the fact that the EventDispatcher interface is limited, you will probably be restricted to wrapping the original EventDispatcher in your own implementation and delegating when necessary. I've seen this done in other people's code (might have even seen it in JavaFX itself) but I can't remember the code well enough to give you examples of this.

如果您要从头开始创建自己的 EventTarget,那么您必须实现您自己的 EventDispatcher.如果你这样做,要记住一些事情需要你自己的实现:

If you are creating your own EventTarget from scratch then you will have to implement your own EventDispatcher. Some things to keep in mind if you do need your own implementation:

  • 它必须只调用为当前阶段注册的 EventHandlers(如果必须有阶段)
  • 它必须只调用为 EventEventType 注册的 EventHandler并说 EventType 的超类型
  • 它必须将Event转发到尾部EventDispatchChain
  • 如果Event被消费了,它必须只返回null
  • 必须能够被同一个Thread并发修改
    • 这是因为 EventHandler 可能会删除自身或在其 handle 方法正在执行时添加/删除另一个 EventHandler.这将在 EventDispatcher 迭代 EventHandler 时发生以某种方式.
    • It must only invoke the EventHandlers registered for the current phase (if there are to be phases)
    • It must only invoke the EventHandlers registered for the Event's EventType and said EventType's supertypes
    • It must forward the Event to the tail EventDispatchChain
    • It must only return null if the Event has been consumed
    • It must be capable of concurrent modification by the same Thread
      • The reason for this is because an EventHandler may remove itself or add/remove another EventHandler while its handle method is executing. This will take place while the EventDispatcher is iterating the EventHandlers in some way.
      • 注意:我不确定这是否真的是合同的一部分,可能不是如果您的实施不需要它,则是必要的.

      这篇关于JavaFX - EventDispatcher 和 EventFilter 之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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