如何发出和处理自定义事件?
javafx 中有几个预定义的事件类。Event.ANY, KeyEvent.KEY_TYPED, MouseEvent.ANY 等等。还有针对事件的高级过滤和处理系统。我想重用它来发送一些自定义信号。
如何创建自定义事件类型 CustomEvent.Any,以编程方式发出此事件并在节点中处理它?
javafx 中有几个预定义的事件类。Event.ANY, KeyEvent.KEY_TYPED, MouseEvent.ANY 等等。还有针对事件的高级过滤和处理系统。我想重用它来发送一些自定义信号。
如何创建自定义事件类型 CustomEvent.Any,以编程方式发出此事件并在节点中处理它?
通常:
一些解释:
如果要创建事件级联,请从“全部”或“任何”类型开始,这将是所有事件类型的根:
EventType<MyEvent> OPTIONS_ALL = new EventType<>("OPTIONS_ALL");
这样就可以创建此类型的后代:
EventType<MyEvent> BEFORE_STORE = new EventType<>(OPTIONS_ALL, "BEFORE_STORE");
然后编写类(扩展 )。应将 EventTypes 键入到此事件类(如我的示例所示)。MyEvent
Event
现在使用(或换句话说:fire)事件:
Event myEvent = new MyEvent();
Node node = ....;
node.fireEvent(myEvent);
如果要捕获此事件:
Node node = ....;
node.addEventHandler(OPTIONS_ALL, event -> handle(...));
node.addEventHandler(BEFORE_STORE, event -> handle(...));
下面是一个(稍微过于复杂)的示例应用程序,演示了 eckig 在他的(优秀)答案中概述的一些概念。
该示例创建了一个视野,该视野是反应堆节点的平铺窗格。自定义闪电事件定期发送到随机节点,该节点在收到事件时将闪烁黄色。筛选器和处理程序将添加到父字段,并将其调用报告给 system.out,以便您可以看到事件冒泡和捕获操作中的阶段。
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();
}
}
}