【问题标题】:How to set ComboBox editor event handler如何设置组合框编辑器事件处理程序
【发布时间】: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 获取ClassFields 并搜索其名称与fx:ids 匹配的名称。当找到匹配项时,它会尝试将Field 反射性地设置为从 FXML 元素创建的对象。 这种注入只发生在控制器 instance 上——FXMLLoader 不知道应用程序中的每个对象实例
  • 请注意,@FXML 注释仅在字段或方法为非公开时才需要。注释基本上只是告诉FXMLLoader 可以尝试访问非公共成员。此外,您假设您的问题与线程有关——事实并非如此。 FXMLLoader 所做的一切都发生在名为 load 的线程上。换句话说,您显示的代码是顺序的。有关 FXML 基础知识的更多信息,请阅读Introduction to FXML

标签: java javafx fxml


【解决方案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);

这是修改后的 Main 类:

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);
}

这是整个 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);
    }
}

【讨论】:

  • 另一个选项是在控制器的(无参数)initialize() 方法中设置KEY_TYPED 处理程序。 FXMLLoader 将在字段注入后调用所述方法,这意味着 ComboBox 将可用。这样做意味着您不再需要从 start 方法调用控制器(至少在这方面)。
  • @Slaw 或更进一步,将处理程序直接挂在 fxml 中 .. 这似乎有效(但不确定是否可能有任何不需要的副作用)
  • hmm ...重新阅读问题:您确定scenebuilder不允许引用吗?从来没有用过,但听起来像是一个重大的遗漏..
  • Scene Builder 让您只需从弹出菜单中选择可用方法即可将处理程序分配给事件。 onKeyTyped 有一个事件(在许多其他事件中),但这是一个在整个组合框中键入的键。在文本字段中键入时不会触发该事件。我认为,只要您有一个混合了两个或多个其他小部件的 UI 小部件,这就是一个问题。
【解决方案2】:

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

以下 sn-p 将 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 中的worksforme(没有测试任何其他)

【讨论】:

  • 有趣。不知道fx:reference 可以用来做这个。
  • @Slaw 我也没有 :)
  • 你的权利我要删除它,所以我也不会混淆任何人我只是好奇我得到一个无法解决 combo.editor 任何关于为什么的想法?
  • @Matt no, sry .. 与 fxml 中的那些确切行一起工作。你有哪个版本?我的是 fx11,可能有什么改变/修复?
  • 啊,也许这只是 fx 11 然后我还在 8
猜你喜欢
  • 2014-01-27
  • 1970-01-01
  • 1970-01-01
  • 2017-07-09
  • 2022-06-18
  • 2011-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多