我是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");
}
}
答案 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();
}
});