JavaFX - EventDispatcher 和 EventFilter 之间的区别 [英] JavaFX - Difference between EventDispatcher and 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 EventHandler
s. 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
是为当前Event
的EventType
注册的,或者超类型之一(即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 currentEvent
'sEventType
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.
这条路径的计算方式是通过实现EventDispatchChain
和 EventTarget
.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 中的实现(Window
、Scene
、Node
、MenuItem
等...) 提供他们自己的 EventDispatcher
实现.在这方面你不必担心如何使用EventDispatcher
.你不甚至直接使用它.相反,您通过添加 EventHandler
s[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 EventHandler
s via the
[add|remove]EventHandler
and [add|remove]EventFilter
methods as well as
he various onXXX
properties.
当 buildEventDispatchChain
被调用时,比如一个 Button
,Button
在前面它的 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 EventDispatcher
s. 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 EventDispatcher
s 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;
}
步骤是:
- 为捕获阶段调度
Event
- 这会调用作为过滤器 添加的所有
- 在此处使用
Event
不会停止Event
在这一步;但会阻止Event
在后面的步骤中被处理
EventHandler
- Dispatch the
Event
for the capturing phase- This invokes all the
EventHandler
s that were added as a filter - Consuming an
Event
here will not stop the dispatching of theEvent
in this step; but will stop theEvent
from being processed in later steps
- This invokes all the
- 如果返回的
Event
为null
,则表示Event
已被消耗链下游的某个地方,无需进行更多处理 - 这会调用作为 handler 添加的所有
EventHandler
s(其中包括通过onXXX
属性添加的那些) - 与第 1 步一样,在此处使用
Event
不会停止处理这一步;但会阻止Event
在后面的步骤中被处理
- If the returned
Event
wasnull
that means theEvent
was consumed somewhere down the chain and no more processing is to be done - This invokes all the
EventHandler
s that where added as a handler (which includes the ones added via theonXXX
properties) - As with step #1, consuming an
Event
here does not stop the processing of this step; but will stop theEvent
from being processed in later steps
- 与
EventDispatchChain
一样,返回null
意味着Event
已经Event
的消耗和处理必须停止
- As with
EventDispatchChain
, returningnull
means theEvent
has been consumed and processing of theEvent
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 EventHandler
s. 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:
- 它必须只调用为当前阶段注册的
EventHandler
s(如果必须有阶段) - 它必须只调用为
Event
的EventType
注册的EventHandler
并说EventType
的超类型 - 它必须将
Event
转发到尾部EventDispatchChain
- 如果
Event
被消费了,它必须只返回null
- 必须能够被同一个
Thread
并发修改- 这是因为
EventHandler
可能会删除自身或在其handle
方法正在执行时添加/删除另一个EventHandler
.这将在EventDispatcher
迭代EventHandler
时发生以某种方式.
- It must only invoke the
EventHandler
s registered for the current phase (if there are to be phases) - It must only invoke the
EventHandler
s registered for theEvent
'sEventType
and saidEventType
's supertypes - It must forward the
Event
to the tailEventDispatchChain
- It must only return
null
if theEvent
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 anotherEventHandler
while itshandle
method is executing. This will take place while theEventDispatcher
is iterating theEventHandler
s in some way.
- 注意:我不确定这是否真的是合同的一部分,可能不是如果您的实施不需要它,则是必要的.
这篇关于JavaFX - EventDispatcher 和 EventFilter 之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- The reason for this is because an
- 这是因为