Java 如何发出和处理自定义事件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27416758/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to emit and handle custom events?
提问by ayvango
There are several predefined event classes in javafx. Event.ANY, KeyEvent.KEY_TYPED, MouseEvent.ANY and so on. There is also advanced filtering and handling system for events. And I'd like to reuse it to send some custom signals.
javafx 中有几个预定义的事件类。Event.ANY、KeyEvent.KEY_TYPED、MouseEvent.ANY 等等。还有先进的事件过滤和处理系统。我想重用它来发送一些自定义信号。
How can I create my custom event type CustomEvent.Any, emit this event programmatically and handle it in a node?
如何创建自定义事件类型 CustomEvent.Any,以编程方式发出此事件并在节点中处理它?
采纳答案by eckig
In general:
一般来说:
- Create a desired EventType.
- Create the corresponding Event.
- Call Node.fireEvent().
- Add Handlersand/or Filtersfor EventTypes of interest.
- 创建所需的EventType。
- 创建相应的Event。
- 调用Node.fireEvent()。
- 为感兴趣的事件类型添加处理程序和/或过滤器。
Some explanations:
一些解释:
If you want to create an event cascade, start with an "All" or "Any" type, that will be the root of all of the EventTypes:
如果要创建事件级联,请从“All”或“Any”类型开始,这将是所有 EventType 的根:
EventType<MyEvent> OPTIONS_ALL = new EventType<>("OPTIONS_ALL");
This makes possible creating descendants of this type:
这使得创建这种类型的后代成为可能:
EventType<MyEvent> BEFORE_STORE = new EventType<>(OPTIONS_ALL, "BEFORE_STORE");
Then write the MyEvent
class (which extends Event
). The EventTypes should be typed to this event class (as is my example).
然后编写MyEvent
类(扩展Event
)。EventTypes 应该输入到这个事件类中(就像我的例子一样)。
Now use (or in other words: fire) the event:
现在使用(或换句话说:触发)事件:
Event myEvent = new MyEvent();
Node node = ....;
node.fireEvent(myEvent);
If you want to catch this event:
如果你想抓住这个事件:
Node node = ....;
node.addEventHandler(OPTIONS_ALL, event -> handle(...));
node.addEventHandler(BEFORE_STORE, event -> handle(...));
回答by jewelsea
Here is a (slightly over-complicated) sample application demonstrating some of the concepts that eckig outlines in his (excellent) answer.
这是一个(稍微过于复杂的)示例应用程序,展示了 eckig 在他的(优秀)答案中概述的一些概念。
The sample creates a visual field which is a tiled pane of reactor nodes. A custom lightning event is periodically sent to a random node, which will flash yellow when it receives the event. Filters and handlers are added to the parent field and their invocation reported to system.out so that you can see the event bubbling and capturing phases in action.
该示例创建了一个视野,它是一个平铺的反应堆节点窗格。自定义闪电事件会定期发送到随机节点,该节点在收到事件时会闪烁黄色。过滤器和处理程序被添加到父字段,它们的调用报告给 system.out,以便您可以看到事件冒泡和捕获阶段的动作。
The code for the LightningEvent itself was mainly copied directly from the standard ActionEvent code in the JavaFX source, your event code could probably be a little simpler.
LightningEvent 本身的代码主要是直接从 JavaFX 源中的标准 ActionEvent 代码复制而来的,您的事件代码可能会更简单一些。
import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
public class LightningSimulator extends Application {
private static final int FIELD_SIZE = 10;
private static final Random random = new Random(42);
@Override
public void start(Stage stage) throws Exception {
TilePane field = generateField();
Scene scene = new Scene(field);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
field.addEventFilter(
LightningEvent.PLASMA_STRIKE,
event -> System.out.println(
"Field filtered strike: " + event.getI() + ", " + event.getJ()
)
);
field.addEventHandler(
LightningEvent.PLASMA_STRIKE,
event -> System.out.println(
"Field handled strike: " + event.getI() + ", " + event.getJ()
)
);
periodicallyStrikeRandomNodes(field);
}
private void periodicallyStrikeRandomNodes(TilePane field) {
Timeline timeline = new Timeline(
new KeyFrame(
Duration.seconds(0),
event -> strikeRandomNode(field)
),
new KeyFrame(
Duration.seconds(2)
)
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
private void strikeRandomNode(TilePane field) {
LightningReactor struckNode = (LightningReactor)
field.getChildren()
.get(
random.nextInt(
FIELD_SIZE * FIELD_SIZE
)
);
LightningEvent lightningStrike = new LightningEvent(
this,
struckNode
);
struckNode.fireEvent(lightningStrike);
}
private TilePane generateField() {
TilePane field = new TilePane();
field.setPrefColumns(10);
field.setMinWidth(TilePane.USE_PREF_SIZE);
field.setMaxWidth(TilePane.USE_PREF_SIZE);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
field.getChildren().add(
new LightningReactor(
i, j,
new StrikeEventHandler()
)
);
}
}
return field;
}
private class LightningReactor extends Rectangle {
private static final int SIZE = 20;
private final int i;
private final int j;
private FillTransition fillTransition = new FillTransition(Duration.seconds(4));
public LightningReactor(int i, int j, EventHandler<? super LightningEvent> lightningEventHandler) {
super(SIZE, SIZE);
this.i = i;
this.j = j;
Color baseColor =
(i + j) % 2 == 0
? Color.RED
: Color.WHITE;
setFill(baseColor);
fillTransition.setFromValue(Color.YELLOW);
fillTransition.setToValue(baseColor);
fillTransition.setShape(this);
addEventHandler(
LightningEvent.PLASMA_STRIKE,
lightningEventHandler
);
}
public void strike() {
fillTransition.playFromStart();
}
public int getI() {
return i;
}
public int getJ() {
return j;
}
}
private class StrikeEventHandler implements EventHandler<LightningEvent> {
@Override
public void handle(LightningEvent event) {
LightningReactor reactor = (LightningReactor) event.getTarget();
reactor.strike();
System.out.println("Reactor received strike: " + reactor.getI() + ", " + reactor.getJ());
// event.consume(); if event is consumed the handler for the parent node will not be invoked.
}
}
static class LightningEvent extends Event {
private static final long serialVersionUID = 20121107L;
private int i, j;
public int getI() {
return i;
}
public int getJ() {
return j;
}
/**
* The only valid EventType for the CustomEvent.
*/
public static final EventType<LightningEvent> PLASMA_STRIKE =
new EventType<>(Event.ANY, "PLASMA_STRIKE");
/**
* Creates a new {@code LightningEvent} with an event type of {@code PLASMA_STRIKE}.
* The source and target of the event is set to {@code NULL_SOURCE_TARGET}.
*/
public LightningEvent() {
super(PLASMA_STRIKE);
}
/**
* Construct a new {@code LightningEvent} with the specified event source and target.
* If the source or target is set to {@code null}, it is replaced by the
* {@code NULL_SOURCE_TARGET} value. All LightningEvents have their type set to
* {@code PLASMA_STRIKE}.
*
* @param source the event source which sent the event
* @param target the event target to associate with the event
*/
public LightningEvent(Object source, EventTarget target) {
super(source, target, PLASMA_STRIKE);
this.i = ((LightningReactor) target).getI();
this.j = ((LightningReactor) target).getJ();
}
@Override
public LightningEvent copyFor(Object newSource, EventTarget newTarget) {
return (LightningEvent) super.copyFor(newSource, newTarget);
}
@Override
public EventType<? extends LightningEvent> getEventType() {
return (EventType<? extends LightningEvent>) super.getEventType();
}
}
}