Java 如何设置组合框编辑器事件处理程序

Java 如何设置组合框编辑器事件处理程序,java,javafx,fxml,Java,Javafx,Fxml,我试图在一个简单的JavaFX应用程序中将一个键事件处理程序添加到一个可编辑的组合框中。由于场景生成器不提供对组合框中文本字段的访问,因此我必须在代码中添加事件处理程序 下面是我添加处理程序的尝试 主类 控制器类 FXML(sample.FXML) 这会在start方法的最后一行出现空指针异常时崩溃。问题是,combo还没有值,因为(我假设)FXML加载程序在一个单独的线程中运行,并且在我的代码尝试调用getEditor时还没有完成 设置事件处理程序的更合适方法是什么 编辑:添加了完整的源代

我试图在一个简单的JavaFX应用程序中将一个键事件处理程序添加到一个可编辑的组合框中。由于场景生成器不提供对组合框中文本字段的访问,因此我必须在代码中添加事件处理程序

下面是我添加处理程序的尝试

主类 控制器类 FXML(sample.FXML)

这会在start方法的最后一行出现空指针异常时崩溃。问题是,
combo
还没有值,因为(我假设)FXML加载程序在一个单独的线程中运行,并且在我的代码尝试调用getEditor时还没有完成

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


编辑:添加了完整的源代码方法需要访问控制器类中声明的ComboBox变量。仅仅在主类中用
@FXML
注释组合框变量是不够的

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

public ComboBox getComboBox()
{
    return combo;
}
在Main类中,使用此方法访问组合框下面的编辑器:

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所建议的,另一个(更好的)解决方案是在控制器中设置keyTyped处理程序。使用
initialize
方法,如果存在,加载程序会自动调用该方法

public void initialize()
{
    combo.getEditor().setOnKeyTyped(this::handleComboKeyPress);
}
以下是整个控制器类(减去导入):


另一种选择是直接在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);
}
@FXML
私人组合框组合;
@FXML
私有无效句柄类型(KeyEvent ev){
系统输出打印项次(“ev:+ev);
}


更新:这在fx8中似乎不起作用(感谢Matt的提醒!)-fx11中的工作表单(没有测试任何其他)

您是否定义了
combo
以及它的初始化位置?正如Slaw建议的,Main#combo与Controller#combo不同,当您试图设置它时,Main#combo可能为空。您需要在sample.Controller上设置一个委托,以传递给Controller#combo.getEditor()或类似的内容。@barrybrown它来自您的加载程序。您正在加载sample.fxml,其中设置了fx:controller。这是被注入和初始化的控制器。
fxmloader
知道控制器的
;在您的情况下,从
fx:controller
指定的完全限定名开始。加载FXML文件时,它会为您创建控制器实例。稍后,
fxmloader
获取
类的
字段
s,并搜索名称与
fx:id
s匹配的字段。当找到匹配项时,它会尝试将
字段
反射设置为从FXML元素创建的对象。此注入仅在控制器实例中发生-
FXMLLoader
不知道应用程序中的每个对象实例。注意
@FXML
注释仅在字段或方法为非公共时才有必要。注释基本上只是告诉
fxmloader
可以尝试访问非公共成员。此外,您假设您的问题与线程相关,但事实并非如此。
fxmloader
所做的一切都发生在调用
load
的线程上。换句话说,您显示的代码是连续的。有关FXML基本原理的更多信息,请阅读。另一个选项是在控制器的(无参数)
initialize()
方法中设置
KEY\u-TYPED
处理程序。
fxmloader
将在字段注入后调用所述方法,这意味着
组合框将可用。这样做意味着您不再需要从
start
方法调用控制器(至少在这方面是这样)。@Slaw或更进一步,直接在fxml中钩住处理程序。。这似乎有效(但不确定是否有任何不必要的副作用)嗯。。。重新阅读问题:您确定scenebuilder不允许引用吗?从未使用过它,但听起来像是一个重大遗漏。.场景生成器允许您只需从弹出菜单中选择可用的方法,即可为事件指定处理程序。onKeyTyped(在许多其他事件中)有一个事件,但这是针对组合框上键入的键的事件。在文本字段中键入时不会触发该事件。我认为这是一个问题,任何时候你有一个UI小部件是两个或更多其他小部件的混合体。有趣。不知道
fx:reference
可以用来做这件事。@Slaw me not:)你的权利我会删除它,这样我不会混淆任何人,我只是好奇我得到了一个无法解析的组合。编辑器有什么想法吗?@Matt no,sry。。使用fxml中的精确行创建工作表单。你们有哪个版本?我的是fx11,可能有什么改变/修复了吗?啊,也许这只是fx11,然后我仍然在8
public void initialize()
{
    combo.getEditor().setOnKeyTyped(this::handleComboKeyPress);
}
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);
    }
}
<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);
}