目的
EventDispatcher
顾名思义,
事件调度程序
是发送一个
Event
.这样做的方式取决于实现。但是,
标准实现(JavaFX内部)使得
事件调度程序
一种
“收藏”
EventHandler
S.这是
事件调度程序
引起赞许
事件处理程序
在正确的时间。是什么造成的
一个
事件处理程序
“适当”取决于:
-
如果
事件处理程序
已为事件的当前阶段注册
调度周期(捕获或冒泡)
-
如果
事件处理程序
是为当前
事件
的
EventType
或
超级类型之一(即
EventType.getSuperType()
)
事件路径(或“链”)。
如果我们集中在场景图上,当
事件
是从顶部开始的
场景图的
Window
)在等级制度中
目标(通常是
Node
但至少实现了
EventTarget
)。这是事件调度周期的“捕获”阶段。之后
到达目标后,它将返回场景图,直到到达
窗口
再来一次。这是循环的“冒泡”阶段。
捕获阶段
-
窗口
->
Scene
->
Root Node
->
Middle Node
->
事件目标
气泡相
-
窗口
<。-
场景
<。-
根节点
<。-
中间节点
<。-
事件目标
如果在任何步骤
事件
消耗(通过
Event.consume()
)那就不会了
转发到下一步。这有效地停止了
事件
在链条中所需的步骤。
计算此路径的方法是通过实现
EventDispatchChain
以及
事件目标
.一个
事件目标
必须实现
以下方法:
EventDispatchChain buildEventDispatchChain(EventDispatchChain tail);
当一个
事件
它有一个指定的
事件目标
.自从
事件目标
将在场景图的底部
自下而上。它通过预先准备一个
事件调度程序
致
事件分派链
在层次结构的每个级别(使用
EventDispatchChain.prepend(EventDispatcher)
)。这是哪里
事件调度程序
开始进来。
每个
事件目标
通常有自己的
事件调度程序
与之相关。这个
的实现
事件目标
标准JavaFX(
窗口
,请
场景
,请
节点
,请
MenuItem
等)提供自己的
事件调度程序
.在
这方面你不用担心如何使用
事件调度程序
.你没有
甚至直接使用。相反,你补充说
事件处理程序
S通过
[add|remove]EventHandler
和
[add|remove]EventFilter
方法以及
他五花八门
onXXX
属性。
什么时候?
buildEventDispatchChain
被召唤说,一个
Button
,的
按钮
预处理
它的
事件调度程序
给给定的
事件分派链
.然后打电话来
生成事件调度链接
关于它
Parent
如果有。这一直持续到
根
节点
的
场景
.根
节点
电话
生成事件调度链接
关于said
场景
在预先准备好
事件调度程序
,上也一样
这个
窗口
它被附加到。
在这一点上
事件分派链
已完全建立并准备好处理
这个
事件
.如果还不明显,
事件分派链
基本上是公正的
一堆
事件调度程序
换句话说,它是一个高度专业化的
java.util.Deque
但实际上没有扩展这个接口。
注:
事件分派链
还提供了
append(EventDispatcher)
方法
对于预处理是错误操作的情况
.
调度事件
一旦
事件分派链
是完全建成的,是时候实际调度
事件
.这是通过在
事件分派链
以下内容:
Event dispatchEvent(Event event);
这个有
事件分派链
抢先(流行)
事件调度程序
在
堆栈和调用
它的
方法:
Event dispatchEvent(Event event, EventDispatchChain tail);
旁注:不幸的是
事件调度程序
的方法具有类似于的签名
EventDispatchChain.dispatchEvent(Event)
可能引起混乱
.
旁注2:
tail
会是一样的
事件分派链
整个
整个过程
.
这就是
事件调度程序
实际使用S。这是算法
由每个人使用
事件调度程序
由内部定义
com.sun.javafx.event.BasicEventDispatcher
类(Java 10源代码):
@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;
}
步骤如下:
-
调度
事件
对于捕获阶段
-
这将调用
事件处理程序
添加为
滤波器
-
消耗
事件
在这里
不会
停止调度
事件
在里面
这一步
但是会阻止
事件
在以后的步骤中处理
-
如果
事件
已消耗,返回
null
,否则转发
事件
到
尾
等它回来
-
如果
tail.dispatchEvent
不回来了
无效的
然后调度
事件
对于
鼓泡阶段
-
如果返回
事件
是
无效的
这意味着
事件
已被消耗
链下的某个地方,不需要再进行处理
-
这将调用
事件处理程序
在这里加了一个
处理程序
(其中
包括通过添加的
在XXX
属性)
-
与步骤1一样,使用
事件
在这里
不停止处理
这一步
但是会阻止
事件
在以后的步骤中处理
-
如果
事件
是消费回报
无效的
,否则返回
事件
-
和…一样
事件分派链
,返回
无效的
是指
事件
已经
消耗和处理
事件
必须停止
完成了
为每个
事件调度程序
在
事件分派链
.电话
到
尾差
基本上是一个“递归”操作(而不是
“实”递归)。实际上,这段代码沿着堆栈(调用
尾差
)然后走回堆栈(当
尾差
返回)。每个“链中的链接”在“递归”之前进行处理
调用(捕获阶段)并在“递归”调用返回(冒泡
阶段)。
不过,请注意,在每一步,
事件调度程序
那就是
实际调用每个适当的
事件处理程序
S.
这就是
事件调度程序
使用
.
实现你自己的
事件调度程序
扩展已实现的类时
事件目标
那你就应该
创建自己的
事件调度程序
什么时候?
绝对需要
.如果你的目标是
控制
事件
达到一定程度
事件目标
那么你的第一选择应该是
将消耗
事件
在适当的地方(JAI在
评论)。如果你想改变
事件
那么你呢
可能需要提供自己的
事件调度程序
.但是,由于关闭
内部的性质
事件调度程序
实现,加上事实
那个
事件调度程序
接口有限,您可能会受到限制
包装原件
事件调度程序
在您自己的实施和授权中
必要时。我在别人的代码中看到过这种情况
它在javafx本身)但是我记不清代码是否足够好
这方面的例子。
如果您正在创建
拥有
事件目标
你必须从头开始
实现你自己的
事件调度程序
.如果你这样做的话,要记住一些事情
需要您自己的实施:
-
它必须只调用
事件处理程序
为当前阶段注册的(如果
有阶段)
-
它必须只调用
事件处理程序
为注册的
事件
的
事件类型
说
事件类型
的超类型
-
它必须向前
事件
到尾部
事件分派链
-
它必须只返回
无效的
如果
事件
已被消耗
-
一定是
可同时修改
Thread
-
这是因为
事件处理程序
可能会自行移除或
添加/删除另一个
事件处理程序
当它
handle
方法正在执行。这个
将发生在
事件调度程序
正在迭代
事件处理程序
S
在某种程度上。
-
它必须“修复源”
事件
.在标准JavaFX实现中
每个
事件调度程序
更新的源
事件
成为
Object
与之相关的
事件调度程序
(例如
节点
)。这是在
上述子类
com.sun.javafx.event.basicEventDispatcher
.
“修复源”的方法是
Event.copyFor(Object, EventTarget)
-
注:我不确定这是否是合同的一部分,可能不是
如果您的实现不需要它,则是必需的。