【发布时间】:2019-05-08 17:47:15
【问题描述】:
我正在尝试在一个简单的 JavaFX 应用程序中将一个键事件处理程序添加到一个可编辑的 ComboBox。由于 Scene Builder 不提供对 ComboBox 中 TextField 的访问,因此我必须在代码中添加事件处理程序。
这是我添加处理程序的尝试。
主类
package sample;
import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.stage.Stage;
public class Main extends Application {
@FXML
private ComboBox combo;
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 400, 300));
primaryStage.show();
Controller c = loader.getController();
combo.getEditor().setOnKeyTyped(c::handleComboKeyPress);
}
public static void main(String[] args) {
launch(args);
}
}
控制器类
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.input.KeyEvent;
public class Controller {
@FXML
private ComboBox combo;
public void handleComboKeyPress(KeyEvent ke)
{
System.out.print("key press. "); // debugging
String query = combo.getEditor().getText();
System.out.println(query); // debugging
}
}
FXML (sample.fxml)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.layout.Pane?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<ComboBox fx:id="combo" editable="true" layoutX="75.0" layoutY="34.0" prefWidth="150.0" promptText="City, State" visibleRowCount="5" />
</children>
</Pane>
这会在 start 方法的最后一行出现空指针异常而崩溃。问题是combo 还没有值,因为(我假设)FXML 加载器在单独的线程中运行,并且在我的代码尝试调用 getEditor 时还没有完成。
设置事件处理程序的更合适的方法是什么?
编辑:添加完整的源代码
【问题讨论】:
-
您是否定义了
combo以及它的初始化位置? -
Slaw 建议 Main#combo 与 Controller#combo 不同,当您尝试设置 Main#combo 时,它可能为空。您需要在您的 sample.Controller 上设置一个委托以传递给 Controller#combo.getEditor() 或类似的东西。
-
@barrybrown 它来自您的装载机。您正在加载具有 fx:controller 集的 sample.fxml。那就是被注入和初始化的控制器。
-
FXMLLoader知道您的控制器的Class;在您的情况下,来自fx:controller指定的完全限定名称。当您加载 FXML 文件时,它会为您创建一个控制器实例。稍后,FXMLLoader获取Class的Fields 并搜索其名称与fx:ids 匹配的名称。当找到匹配项时,它会尝试将Field反射性地设置为从 FXML 元素创建的对象。 这种注入只发生在控制器 instance 上——FXMLLoader不知道应用程序中的每个对象实例。 -
请注意,
@FXML注释仅在字段或方法为非公开时才需要。注释基本上只是告诉FXMLLoader可以尝试访问非公共成员。此外,您假设您的问题与线程有关——事实并非如此。FXMLLoader所做的一切都发生在名为load的线程上。换句话说,您显示的代码是顺序的。有关 FXML 基础知识的更多信息,请阅读Introduction to FXML。