如何发出和处理自定义事件?

时间:2014-12-11 06:35:11

标签: java events javafx

javafx中有几个预定义的事件类。 Event.ANY,KeyEvent.KEY_TYPED,MouseEvent.ANY等。还有用于事件的高级过滤和处理系统。我想重复使用它来发送一些自定义信号。

如何创建自定义事件类型CustomEvent.Any,以编程方式发出此事件并在节点中处理它?

2 个答案:

答案 0 :(得分:40)

一般来说:

  1. 创建所需的EventType
  2. 创建相应的Event
  3. 致电Node.fireEvent()
  4. 为感兴趣的事件类型添加Handlers和/或Filters
  5. 一些解释:

    如果您想创建一个事件级联,请从"全部"开始。或"任何" type,它将是所有EventTypes的根:

    EventType<MyEvent> OPTIONS_ALL = new EventType<>("OPTIONS_ALL");
    

    这样就可以创建这种类型的后代:

    EventType<MyEvent> BEFORE_STORE = new EventType<>(OPTIONS_ALL, "BEFORE_STORE");
    

    然后编写MyEvent类(扩展Event)。 EventTypes应该输入到这个事件类中(就像我的例子一样)。

    现在使用(或换句话说:开火)事件:

    Event myEvent = new MyEvent();
    Node node = ....;
    node.fireEvent(myEvent);
    

    如果你想抓住这个事件:

    Node node = ....;
    node.addEventHandler(OPTIONS_ALL, event -> handle(...));
    node.addEventHandler(BEFORE_STORE, event -> handle(...));
    

答案 1 :(得分:20)

这是一个(稍微过于复杂)的示例应用程序,展示了eckig在其(优秀)答案中概述的一些概念。

该示例创建一个视野,该视野是反应堆节点的平铺窗格。自定义闪电事件会定期发送到随机节点,该节点在收到事件时会闪烁黄色。过滤器和处理程序将添加到父字段,并将其调用报告给system.out,以便您可以查看事件冒泡和捕获阶段。

LightningEvent本身的代码主要直接从JavaFX源代码中的标准ActionEvent代码复制,您的事件代码可能会更简单一些。

enter image description here

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();
        }

    }

}