如何设置ComboBox编辑器事件处理程序

时间:2019-05-08 17:47:16

标签: java javafx fxml

我试图在一个简单的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时还没有完成。

设置事件处理程序的更合适的方法是什么?

编辑:添加了完整的源代码

2 个答案:

答案 0 :(得分:1)

start方法需要访问Controller类中声明的ComboBox变量。在Main类中仅用@FXML注释ComboBox变量是不够的。

我通过向Controller类添加getComboBox方法解决了该问题。这将返回由combo变量引用的ComboBox实例。

public ComboBox getComboBox()
{
    return combo;
}

在Main类中,使用此方法进入ComboBox基础的编辑器:

Controller c = loader.getController();
c.getComboBox().getEditor().setOnTyped(c::handleComboKeyPress);

这是修改后的主类:

public class Main extends Application {

    @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();
        c.getComboBox().getEditor().setOnKeyTyped(c::handleComboKeyPress);
    }

更好的解决方案

如下面的@Slaw所建议的那样,另一种(更好的)解决方案是在Controller中设置keyTyped处理程序。使用initialize方法,该方法会由加载程序自动调用(如果存在)。

public void initialize()
{
    combo.getEditor().setOnKeyTyped(this::handleComboKeyPress);
}

这是整个Controller类(减去导入):

public class Controller {
    @FXML
    private ComboBox combo;

    public void handleComboKeyPress(KeyEvent ke)
    {
        // Do stuff
        System.out.println("key pressed.");
    }

    public void initialize()
    {
        combo.getEditor().setOnKeyTyped(this::handleComboKeyPress);
    }
}

答案 1 :(得分:1)

另一种选择是直接将事件处理程序挂接到fxml中。不确定是否在所有情况下都是安全的(不是fxml gal,只是感兴趣:)-但是fx:reference似乎允许访问元素的(任意?)属性。

以下代码段将keyTyped的eventHandler设置为组合的编辑器:

<ComboBox fx:id="combo" editable="true" />
<fx:reference source="combo.editor" onKeyTyped="#handleTyped" /> 

除了处理程序方法之外,这还使控制器无需编写任何代码:

@FXML 
private ComboBox<String> combo;

@FXML 
private void handleTyped(KeyEvent ev) {
    System.out.println("ev: " + ev);
}

更新:这在fx8中似乎不起作用(感谢Matt,要注意!)-在fx11中有效(没有进行任何其他测试)