我想在我的场景中听一些KeyEvent,说KeyCode.ESCAPE
(按下时关闭场景)。
scene.addEventHandler(KeyEvent.ANY, event -> {
if (event.isConsumed())
return;
switch (event.getCode()) {
case ESCAPE:
stage.hide();
event.consume();
break;
default:
break;
}
});
现在,场景中的节点也可以收听ESCAPE
。
// ....
someOtherNode.addEventHandler(KeyEvent.ANY, e -> {
if (e.getCode() == KeyCode.ESCAPE) {
// do stuff
e.consume();
}
});
// ....
如何确保从节点而不是场景中消耗KeyEvent
?
根据Oracle的图表,一种解决方法是在Node层次结尾处添加一个虚拟Node
,以监听KeyCode
s
但是有没有更好的解决方案,比如反转传播路径?
修改
用例:
阻止其他节点的类似弹出式节点需要侦听ESC键或focusProperty()
,以便它可以自行关闭。
答案 0 :(得分:6)
有两种方法可以影响事件:
使用Node.addEventFilter(...)
方法注册过滤器。过滤器将在事件的捕获阶段执行(因为窗口越来越具体,确定哪些节点应该获取事件)。
使用Node.addEventHandler(...)
方法注册处理程序。处理程序将从捕获阶段中找到的最特定节点开始执行,向下一直到消耗它为止。
因此在捕获阶段,会创建一个堆栈。从窗口(最顶层的父节点)开始,此事件可能执行的每个节点都将添加到堆栈中(以最底层的子节点结束)。过滤器可以中断此过程,或者只是在此过程中执行事件。
在冒泡阶段,事件处理程序将从堆栈顶部(在捕获阶段创建)开始触发,直到堆栈为空或事件被消耗。
在你的情况下,你真的不应该担心。如果任何节点关心处理“ESC”事件,他们将在冒泡阶段这样做(他们应该使用该事件以防止进一步处理)。您可以在ComboBox
中看到此行为。如果他们不在乎,它会冒泡到你的Scene
并且该处理程序将执行。只需确保您创建的处理“ESC”按下的任何自定义代码也会消耗该事件。
有关详细信息,请参阅此处的说明和教程:http://docs.oracle.com/javafx/2/events/jfxpub-events.htm
以下是一些演示Escape功能的示例代码。在聚焦ComboBox
时按ESC不会导致应用程序关闭,而它将关闭其他控件。
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.converter.DefaultStringConverter;
public class FXEventFiltering extends Application {
public static void main(String[] args) { launch(args); }
@Override
public void start(final Stage stage) throws Exception {
//All the controls are added here
VBox box = new VBox();
ComboBox<String> dropdown = new ComboBox<>();
TextField field = new TextField();
CheckBox check = new CheckBox("Check");
RadioButton radio = new RadioButton("Radio!");
TextArea area = new TextArea();
TableView<String> table = new TableView<String>(FXCollections.observableArrayList(new String[]{"one","two"}));
TableColumn<String, String> tc = new TableColumn<String, String>("Column1");
tc.setEditable(true);
tc.setCellFactory(TextFieldTableCell.<String,String>forTableColumn(new DefaultStringConverter()));
tc.setCellValueFactory(new Callback<CellDataFeatures<String,String>, ObservableValue<String>>(){
@Override
public ObservableValue<String> call(CellDataFeatures<String, String> arg0) {
return new SimpleStringProperty(arg0.getValue());
}});
table.getColumns().add(tc);
box.getChildren().addAll(dropdown, field, check, radio, area, table);
//Setting up your scene
Scene scene = new Scene(box);
stage.setScene(scene);
scene.addEventHandler(KeyEvent.ANY, new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
System.out.println("KEYS!" + event.getEventType().getName());
switch (event.getCode()) {
case ESCAPE:
System.out.println("Escape!");
stage.hide();
event.consume();
break;
default:
break;
}
}
});
box.requestFocus(); // Removing default focus
stage.show();
}
}
答案 1 :(得分:0)
也许你可以在场景中捕获事件之后遍历所有节点,找出哪个节点有实际焦点?然后你可以调用node方法来关闭?