button.onActionProperty()。addListener在javafx中不起作用

时间:2014-03-30 13:06:15

标签: java events javafx

我是Java新手,我正在尝试Java事件,但我绝对迷失了。我知道来自C#的事件在哪里没有问题,而且它们完美无缺,但Java是不同的宇宙。我试图在互联网上找到一些东西,但我无法想象它,所以我在这里。

我有一个对象,我需要触发一些动作。触发此操作时,我不仅需要调用一个事件处理程序,还需要调用来自不同对象的更多事件处理程序。

例如,我只使用Button类。

有两种方法可以做到这一点:

一种方法是使用button.setOnAction方法。但这不起作用,因为当我第二次调用此方法(来自另一个对象)时,我只是将一个事件处理程序替换为另一个。你可以在方法initEventsUselessWay()中看到我的意思。

第二种方法是使用button.onActionProperty().addListener。但这根本不起作用。您可以在方法initEventsNeededWay()中看到。

那么,为什么button.onActionProperty().addListener不起作用?

在Javafx中有什么方法可以做到这一点吗?

最后我不会使用Button类,但像MyClass这样的东西需要在这里实现。但如果这不适用于Button类,那么它也无法在MyClas上运行。

非常感谢您的建议。

package sample;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class JavaEventsTest1589 extends Application {
private Button btnDemo1;
private Button btnDemo2;

@Override
public void start(Stage primaryStage) {
    // panel
    Pane rootPane = new Pane();

    // scene
    Scene scene = new Scene(rootPane, 300, 250);
    primaryStage.setTitle("events demo");
    primaryStage.setScene(scene);

    // button 1
    btnDemo1 = new Button();
    rootPane.getChildren().add(btnDemo1);
    btnDemo1.setText("Execute Demo 1");
    btnDemo1.setLayoutX(50);
    btnDemo1.setLayoutY(10);

    // button 2
    btnDemo2 = new Button();
    rootPane.getChildren().add(btnDemo2);
    btnDemo2.setText("Execute Demo 2");
    btnDemo2.setLayoutX(50);
    btnDemo2.setLayoutY(50);

    initEventsUselessWay();

    initEventsNeededWay();

    primaryStage.show();
}

public static void main(String[] args) {
    launch(args);
}

private void initEventsUselessWay() {

    btnDemo1.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            runDemoPrimaryReaction();
        }
    });

    btnDemo1.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            runDemoSecondaryReaction();
        }
    });
}

private void initEventsNeededWay() {

    btnDemo2.onActionProperty().addListener(new ChangeListener<EventHandler<ActionEvent>>() {
        @Override
        public void changed(ObservableValue<? extends EventHandler<ActionEvent>> observableValue, EventHandler<ActionEvent> actionEventEventHandler, EventHandler<ActionEvent> actionEventEventHandler2) {
            runDemoThisINeed_No1();
        }
    });

    btnDemo2.onActionProperty().addListener(new ChangeListener<EventHandler<ActionEvent>>() {
        @Override
        public void changed(ObservableValue<? extends EventHandler<ActionEvent>> observableValue, EventHandler<ActionEvent> actionEventEventHandler, EventHandler<ActionEvent> actionEventEventHandler2) {
            runDemoThisINeed_No2();
        }
    });

}

private void runDemoPrimaryReaction() {
    System.out.println("useless way - primary reaction");
}

private void runDemoSecondaryReaction() {
    System.out.println("useless way - secondary reaction");
}

private void runDemoThisINeed_No1() {
    System.out.println("not working way - No1");
}

private void runDemoThisINeed_No2() {
    System.out.println("not working way - No2");
}

}

3 个答案:

答案 0 :(得分:7)

使用addEventHandler:

btnDemo1.addEventHandler(ActionEvent.ACTION, new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent actionEvent) {
        runDemoPrimaryReaction();
    }
});

btnDemo1.addEventHandler(ActionEvent.ACTION, new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent actionEvent) {
        runDemoSecondaryReaction();
    }
});

我建议使用Java 8,在这种情况下你可以写

btnDemo1.addEventHandler(ActionEvent.ACTION, event -> runDemoPrimaryReaction());
btnDemo1.addEventHandler(ActionEvent.ACTION, event -> runDemoSecondaryReaction());

setOnAction(...)方法是一种“便利”方法。它的工作方式是Button维护ObjectProperty<EventHandler<ActionEvent>>.如果设置此属性的值(到EventHandler<ActionEvent>),该事件处理程序将自动添加到事件处理程序中按钮。如果你第二次设置它,因为它只是一个属性,它将替换现有的事件处理程序。因此,在只有一个处理程序的情况下,您可以使用它来稍微快一些。 (它也适用于FXML。)

onActionProperty().addListener(...)完全不同:它会侦听属性本身的变化。因此,如果以这种方式注册侦听器,然后调用setOnAction(...),则在调用setOnAction时(而非按下按钮时)将调用您在属性中注册的侦听器。

查看tutorial,特别是第一部分“处理事件”和第二部分“使用便利方法”。第二部分说明了setOnAction实际执行的时间。

答案 1 :(得分:1)

您需要将“动作”视为Button的属性,而不是事件。 Button一次只能有一个动作,因此您将其设置为一个值,然后将其更改为另一个值,这意味着只有第二个处理程序中的代码才会执行​​。

使用其中一个提供内置属性更改通知的JavaFX属性类来实现action属性。当您调用button.onActionProperty().addListener(...时,您正在添加一个将在更改操作时调用的侦听器。

在致电addListener(...之前尝试调用setOnAction(...代码,并且应该清楚发生了什么。

如果你想用C#来考虑这个,那么

  • button.setOnAction(..就像一个属性,例如button.Action = ...
  • button.onActionProperty().addListener(...就像一个事件,例如button.ActionChanged += ...

如果您是JavaFX的新手并且需要一些有关其特定属性和事件实现的帮助,我可以建议以下资源:

<强>更新

阅读完你的评论后,我意识到这不是特定于JavaFX的,而是一般的事件。事件只是观察者模式的实现(even in C#)。

基本实现可能看起来像这样,尽管自己编写这一点没什么意义,因为JavaFX确实包含了一个内置的方式来注册事件的监听器,如James_D所示。

public interface MyListener {
    invoke();
}

class MyButton extends Button {

    List<MyListener> listeners = new ArrayList<MyListener>();

    public void addListener(MyListener toAdd) {
        listeners.add(toAdd);
    }

    private void invoke() {
        for (HelloListener l : listeners)
            l.invoke();
    }

    this.setOnAction(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            this.invoke();
        }
    });
}

答案 2 :(得分:0)

基于OP的评论

  

所以我说明当我在控制台中单击“执行演示1”按钮时,我会在控制台中看到两行:'无用的方式 - 主要反应无用的方式 - 二次反应'

你需要调用同一个事件处理程序中的方法,如下所示:

btnDemo1.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent actionEvent) {
        runDemoPrimaryReaction();
        runDemoSecondaryReaction();
    }
});