好的-我已经为此奋斗了,阅读了一百万篇文章,教程等。它们似乎都没有直接解决我在这里试图做的事情。我有这个小代码示例来说明。
基本上,我希望能够通过按钮引发/触发自定义事件,并让标签响应该事件。
((请注意,除了在实际的Event对象中尝试使用的方法之外,我对寻找其他方法没有兴趣。我非常清楚如何使用更改侦听器等进行此操作,但是我想要学习如何使用这种方法。)
这是代码,您可以看到这些代码并没有给我我想要的结果。其中大部分来自一些示例(对我而言实际上并不奏效),我承认这里有些部分令我头疼。
如果有人可以帮助我使其正常运行,我可以稍后对其进行解剖,以确保我确切地了解发生了什么。这是代码:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
//-------------------------------------------------------------------
public class MyDemo extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// label that should receive the event and react to it
MyLabel lblReceiver = new MyLabel("And I Should Receive & React");
// button to firs the event
Button btnSender = new Button("Initate Event");
btnSender.setPrefWidth(200);
btnSender.setOnAction(e -> {
MyEvent.fireEvent(lblReceiver, e); // really confused what the first parameter here is supposed to be.
});
// set up stage and show it
Stage stage = new Stage();
VBox root = new VBox(btnSender, lblReceiver);
root.setSpacing(10);
root.setPadding(new Insets(10, 10, 10, 10));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
//-------------------------------------------------------------------
// Interface for objects that want to listen to my event
//-------------------------------------------------------------------
interface MyEventListener {
void onMyEvent();
}
//-------------------------------------------------------------------
// My event definition itself
//-------------------------------------------------------------------
class MyEvent extends Event {
public static final EventType<MyEvent> MY_EVENT = new EventType<>(ANY, "MY_EVENT");
public MyEvent(EventType<? extends MyEvent> eventType) {
super(eventType);
}
}
//-------------------------------------------------------------------
//base/parent class for my label - this is what should receive/respond to event and
// where I'm sure i have problems - just don't know what.
//-------------------------------------------------------------------
class MyLabel extends Label implements MyEventListener {
public MyLabel(String name) {
this.setAlignment(Pos.CENTER);
this.setText(name);
this.setPrefWidth(200);
}
@Override // this is what I'm expecting to happen when i click the button
public void onMyEvent() {
this.setText("YAY! i got the event!");
}
private final ObjectProperty<EventHandler<? super MyEvent>> onMyEventProp
= new SimpleObjectProperty<EventHandler<? super MyEvent>>(this, "onMyEvent") {
@Override
protected void invalidated() {
setEventHandler(MyEvent.MY_EVENT, get());
}
};
public final void setOnMyEvent(EventHandler<? super MyEvent> handler) {
onMyEventProp.set(handler);
}
public final EventHandler<? super MyEvent> getOnMyEvent() {
return onMyEventProp.get();
}
public final ObjectProperty<EventHandler<? super MyEvent>> onMyEventProperty() {
return onMyEventProp;
}
}
//-------------------------------------------------------------------
答案 0 :(得分:2)
除了两个问题外,您似乎一切设置正确。
您永远不会添加监听事件的EventHandler
。
实现任意接口不会使您的对象对自定义事件做出反应。事件处理系统不了解您的界面,甚至不知道您已实现它。如果您希望在事件到达标签时调用onMyEvent()
方法,则需要执行以下操作:
public MyLabel() {
//...
addEventHandler(MyEvent.MY_EVENT, event -> onMyEvent());
}
请注意,我使用了addEventHandler
,以便类本身不依赖于onMyEvent
属性。如果您依赖该属性,则外部代码可能会通过替换EventHandler
意外地破坏您的代码。
这使我想知道MyEventListener
接口是否确实必要。您不能只是在EventHandler
内部做您需要做的事情吗?
您实际上从未触发过MyEvent
的实例。
您当前拥有:
btnSender.setOnAction(e -> MyEvent.fireEvent(lblReceiver, e));
这只是将e
(一个ActionEvent
)朝lblReceiver
发射。 fireEvent
方法是static
声明的javafx.event.Event
方法。用MyEvent
前缀方法调用不会更改此处实际调用的方法。将其更改为以下内容:
btnSender.setOnAction(e -> Event.fireEvent(lblReceiver, new MyEvent()));
// or...
btnSender.setOnAction(e -> lblReceiver.fireEvent(new MyEvent()));
// The second option is a convenience method and simply forwards to Event.fireEvent
为了在标签上实际触发您自己的事件类的实例。
JavaFX中的事件处理分为两个阶段:
捕获阶段
Node.addEventFilter(EventType,EventHandler)
之类的方法添加的。冒泡阶段
Node.addEventHandler(EventType,EventHandler)
之类的方法以及诸如Node.onKeyPressed
和ButtonBase.onAction
之类的属性来添加处理程序。事件处理包含以下组件:
javafx.event.Event
(和子类)
实际传递的对象。携带与事件相关的信息(例如MouseEvent
的光标位置)。
Event
也带有源。但是,源是动态的。它始终是当前处理事件的EventHandler
被添加到的对象。因此,添加到EventHandler
的{{1}}将以Button
作为事件的源,但是同一事件将以父事件作为其添加到Button
的事件的源。即使您两次使用相同的EventHandler
实例,此行为也不会改变。
定义EventHandler
类的含义。例如,Event
类型的MouseEvent
意味着鼠标被按下就不足为奇了。 MOUSE_PRESSED
具有超类型;这为每种事件形成了 all 类型的层次结构。注册为超类型的处理程序还将收到子类型的通知。
您不能有两个或更多具有相同超类型和名称的EventType
。这就是为什么实例通常是相应事件类的EventType
字段的原因。
负责将事件调度到适当的处理程序。每个能够成为事件目标的对象通常都有自己的调度程序。例如,每个public static final
,Window
和Scene
实例都有自己的Node
实例(保存在属性中)。
注意:在高度专业化的情况下,您将不必直接处理此界面。
javafx.event.EventDispatchChain
表示事件在目标上触发时将采用的路径。该接口的实例充当EventDispatcher
的堆栈。触发事件时,将基于事件的目标创建并配置新链。在场景图且目标是EventDispatcher
的情况下,堆栈由属于Node
的{{1}},EventDispatcher
和每个{{ 1}}到(父母对孩子)并包括目标。事件沿着该链(捕获阶段)传播,然后备份到该链(冒泡阶段)。
注意:您可能永远不需要直接使用此界面。
事件的实际目标。 Window
接口具有一种用于构建Scene
的方法。换句话说,目标确定事件的路径。这是Node
中的第一个参数。
注意:仅当您要创建可以(显然)成为事件目标的对象(并且该对象并不从EventTarget
的实现扩展而来)时,才需要使用此接口。