web-dev-qa-db-ja.com

カスタムイベントを発行および処理する方法

Javafxにはいくつかの定義済みイベントクラスがあります。 Event.ANY、KeyEvent.KEY_TYPED、MouseEvent.ANYなど。イベント用の高度なフィルタリングおよび処理システムもあります。そして、私はそれをいくつかのカスタム信号を送信するために再利用したいと思います。

カスタムイベントタイプCustomEvent.Anyを作成し、プログラムでこのイベントを発行し、ノードで処理するにはどうすればよいですか?

29
ayvango

一般に:

  1. 目的の EventType を作成します。
  2. 対応する Event を作成します。
  3. Node.fireEvent() を呼び出します。
  4. 興味のあるEventTypeに Handlers および/または Filters を追加します。

いくつかの説明:

イベントカスケードを作成する場合は、「すべて」または「任意」タイプで開始します。これは、すべてのEventTypeのルートになります。

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(...));
41
eckig

以下は、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();
        }

    }

}
21
jewelsea