Как генерировать и обрабатывать пользовательские события?
В javafx есть несколько предопределенных классов событий. Event.ANY, KeyEvent.KEY_TYPED, MouseEvent.ANY и так далее. Существует также усовершенствованная система фильтрации и обработки событий. И я хотел бы использовать его повторно, чтобы послать несколько пользовательских сигналов.
Как я могу создать свой пользовательский тип события CustomEvent.Any, сгенерировать это событие программно и обработать его в узле?
2 ответа
В общем:
- Создайте желаемый EventType.
- Создайте соответствующее событие.
- Вызовите Node.fireEvent ().
- Добавьте обработчики и / или фильтры для интересующих типов событий.
Некоторые объяснения:
Если вы хотите создать каскад событий, начните с типа "Все" или "Любой", который будет корнем всех типов событий:
EventType<MyEvent> OPTIONS_ALL = new EventType<>("OPTIONS_ALL");
Это позволяет создавать потомков этого типа:
EventType<MyEvent> BEFORE_STORE = new EventType<>(OPTIONS_ALL, "BEFORE_STORE");
Затем напишите MyEvent
класс (который расширяется Event
). EventTypes должен быть типизирован для этого класса событий (как в моем примере).
Теперь используйте (или другими словами: fire) событие:
Event myEvent = new MyEvent();
Node node = ....;
node.fireEvent(myEvent);
Если вы хотите поймать это событие:
Node node = ....;
node.addEventHandler(OPTIONS_ALL, event -> handle(...));
node.addEventHandler(BEFORE_STORE, event -> handle(...));
Вот пример (немного чрезмерно сложный) приложения, демонстрирующий некоторые концепции, которые Экиг обрисовывает в общих чертах в своем (превосходном) ответе.
Образец создает поле зрения, представляющее собой плиточную панель узлов реактора. Пользовательское событие молнии периодически отправляется на случайный узел, который будет мигать желтым цветом, когда он получает событие. Фильтры и обработчики добавляются в родительское поле, а их вызов передается в system.out, так что вы можете увидеть фазы всплытия и захвата событий в действии.
Код для самого LightningEvent был в основном скопирован непосредственно из стандартного кода ActionEvent в исходном коде JavaFX, возможно, ваш код события может быть немного проще.
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();
}
}
}