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

Posted

技术标签:

【中文标题】如何发出和处理自定义事件?【英文标题】:How to emit and handle custom events? 【发布时间】:2015-02-09 13:51:14 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

一般:

    创建所需的EventType。 创建对应的Event。 致电Node.fireEvent()。 为感兴趣的 EventType 添加 Handlers 和/或 Filters。

一些解释:

如果要创建事件级联,请从“All”或“Any”类型开始,这将是所有 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(...));

【讨论】:

【参考方案2】:

这是一个(有点过于复杂的)示例应用程序,展示了 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();
        

    


【讨论】:

以上是关于如何发出和处理自定义事件?的主要内容,如果未能解决你的问题,请参考以下文章

Vue:从自定义组件派生的自定义组件中的 v-model 和输入事件

dojo 小部件不会发出事件

如何在黑莓10中处理自定义控件的触摸事件

如何在 iOS 开发中处理自定义事件?

如何在 Angular 中处理自定义输入组件上的输入事件?

如何将自定义参数传递给事件处理程序