Как установить обработчик событий редактора ComboBox - PullRequest
1 голос
/ 08 мая 2019

Я пытаюсь добавить обработчик ключевых событий в редактируемый ComboBox в простом приложении JavaFX. Поскольку Scene Builder не предоставляет доступ к TextField в ComboBox, я должен добавить обработчик событий в коде.

Вот моя попытка добавить обработчик.

Основной класс

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>

Это происходит сбой с исключением нулевого указателя в последней строке метода запуска. Проблема в том, что combo еще не имеет значения, потому что (я предполагаю) загрузчик FXML работает в отдельном потоке и не завершается к тому времени, когда мой код пытается вызвать getEditor.

Что является более подходящим способом установки обработчика событий?

Редактировать: добавлен полный исходный код

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Еще один вариант - подключить обработчик событий непосредственно в fxml. Не совсем уверен, что это безопасно во всех контекстах (не для галочки fxml, просто интересно :) - но fx:reference, кажется, разрешает доступ к (произвольным?) Свойствам элементов.

Следующий фрагмент устанавливает EventHandler для keyTyped для редактора комбо:

<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 (спасибо, Мэтт за хэдс-ап!) - рабочая форма в fx11 (не тестировала другие)

1 голос
/ 08 мая 2019

Методу start необходим доступ к переменной ComboBox, объявленной в классе Controller. Недостаточно просто аннотировать переменную ComboBox с помощью @FXML в классе Main.

Я решил проблему, добавив метод getComboBox в класс Controller. Это вернет экземпляр ComboBox, указанный в переменной combo.

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 ниже, альтернативным (и лучшим) решением является установка обработчика keyTyped в Controller. Используйте метод 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);
    }
}
...