Переменные FXML отображаются как нулевые, несмотря на использование fx: ids - PullRequest
0 голосов
/ 01 мая 2018

Я новичок и пытаюсь создать простое словарное приложение.

У меня есть класс контроллера, в котором есть кнопка для открытия нового диалога для ввода различных деталей.

Диалог управляется отдельным контроллером, и именно здесь я испытываю проблемы, моим элементам FXML присваивается значение null, поэтому при выполнении кода я получаю исключение nullpointerexx. Я аннотировал переменные FXML с помощью @FXML и проверил, совпадает ли fx: id в файле fxml с файлом java.

Вот код Controller.java:

package sample;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.ButtonType;
import javafx.scene.layout.BorderPane;
import javafx.scene.control.Dialog;

import java.io.IOException;
import java.util.Optional;

public class Controller {

@FXML
private BorderPane mainBorderPane;
@FXML
private DialogController controller = new DialogController();
@FXML
public void initialize(){

}

@FXML
public void newItemDialog(){
    Dialog <ButtonType> dialog = new Dialog<>();
    dialog.initOwner(mainBorderPane.getScene().getWindow());
    dialog.setTitle("Insert Word");
    FXMLLoader fxmlLoader = new FXMLLoader();
    fxmlLoader.setLocation(getClass().getResource("newDialog.fxml"));
    try{
        dialog.getDialogPane().setContent(fxmlLoader.load());
    }catch(IOException e) {
        e.printStackTrace();
        return;
    }
    dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
    dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
    Optional<ButtonType> result = dialog.showAndWait();
    if(result.isPresent() && result.get() == ButtonType.OK){
        boolean results = controller.processResults();

    }else{
        System.out.println("Cancelled");
        return;
    }
}
}

Вот код DialogController.java:

package sample;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import sample.control.Data;

import java.io.IOException;
import java.util.Optional;

public class DialogController {
@FXML
private TextField nameField;
@FXML
private TextField descriptionField;
@FXML
private TextField typeField;
@FXML
private TextField sentenceField;


@FXML
public boolean processResults() {

    String name = nameField.getText();
    String description = descriptionField.getText();
    String type = typeField.getText();
    String sentence = sentenceField.getText();

    return Data.getInstance().addWord(name,type,description,sentence);
    }    
}

Вот код примера sample.fxml:

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.ListView?>
<?import java.lang.String?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.VBox?>
<BorderPane fx:controller="sample.Controller"
        xmlns:fx="http://javafx.com/fxml"
        fx:id ="mainBorderPane">

<top>
    <MenuBar>

        <Menu fx:id="fileButton" text="File">
            <MenuItem fx:id="newButton" onAction="#newItemDialog" text="New"/>
            <MenuItem fx:id="updateButton" text="Update"/>
            <MenuItem fx:id="deleteButton" text="Delete"/>
            <MenuItem fx:id="saveButton" text="Save"/>
        </Menu>
        <Menu fx:id="viewButton" text="View">
            <MenuItem text="Dark Mode"/>
        </Menu>
    </MenuBar>
</top>
<left>
    <ListView fx:id="wordListView" BorderPane.alignment="TOP_LEFT" prefHeight="Infinity" prefWidth="250">

    </ListView>
</left>
<center>
    <VBox>
        <TextArea fx:id = "wordDetails" prefWidth="Infinity" prefHeight="400">

        </TextArea>
        <TextArea prefWidth="Infinity" prefHeight="200">

        </TextArea>
    </VBox>
</center>

</BorderPane>

Вот код newDialog.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.*?>

<?import javafx.scene.text.Text?>
<?import javafx.scene.control.DialogPane?>
<DialogPane xmlns="http://javafx.com/javafx"
        xmlns:fx="http://javafx.com/fxml"
        fx:controller="sample.DialogController"
        prefHeight="400.0" prefWidth="600.0">
<headerText>
    Enter A Word
</headerText>
<content>
    <VBox>
        <TextField fx:id="nameField" promptText="Enter the word"/>
        <TextField fx:id="typeField" promptText="Enter the type"/>
        <TextField fx:id="descriptionField" promptText="Enter a description"/>
        <TextField fx:id="sentenceField" promptText="Enter a sentence"/>
    </VBox>
</content>

Заранее спасибо!

1 Ответ

0 голосов
/ 01 мая 2018

Аннотация @FXML указывает поле, которое должно быть инициализировано FXMLLoader для ссылки на объекты, созданные в соответствии с элементами в файле FXML при загрузке файла FXML. Следовательно:

  1. Имеет смысл аннотировать поля @FXML, только если они соответствуют элементам в файле FXML
  2. Это никогда имеет смысл инициализировать поле, если оно аннотировано @FXML (потому что FXMLLoader должно его инициализировать)

Ваше DialogController поле

@FXML
private DialogController controller = new DialogController();

не имеет смысла по обеим этим причинам: в sample.fxml нет элемента DialogController, и если бы он был, не было бы смысла инициализировать поле.

Контроллер - это определенный объект , связанный с пользовательским интерфейсом, загруженным из файла FXML. Ассоциация устанавливается FXMLLoader при загрузке файла FXML. Если вы загружаете файл FXML несколько раз (как вы, похоже, делаете здесь, поскольку вы загружаете newDialog.fxml в обработчик событий), то (конечно) вы каждый раз получаете новые экземпляры всех элементов в FXML, и, следовательно, каждый раз новый экземпляр класса контроллера.

Объект, который вы создаете с помощью

@FXML private DialogController controller = new DialogController();

не является контроллером для любого пользовательского интерфейса, загруженного в любое время, когда вы загружаете newDialog.fxml; это просто еще один объект того же класса. Не ясно, ожидаете ли вы, что это поле будет каким-либо образом ссылаться на контроллер, созданный при первом выборе пользователем "New" из меню, или на тот, который был создан при втором выборе этого пункта меню, и т. Д. Или, конечно, как вы ожидается, что он будет ссылаться на любой из этих контроллеров, когда вы инициализируете его до того, как загрузите newDialog.fxml.

Вы получаете контроллер от FXMLLoader после загрузки FXML. Так что вам просто нужно:

public class Controller {

    @FXML
    private BorderPane mainBorderPane;
    @FXML
    public void initialize(){

    }

    @FXML
    public void newItemDialog(){
        Dialog <ButtonType> dialog = new Dialog<>();
        dialog.initOwner(mainBorderPane.getScene().getWindow());
        dialog.setTitle("Insert Word");
        FXMLLoader fxmlLoader = new FXMLLoader();
        fxmlLoader.setLocation(getClass().getResource("newDialog.fxml"));
        try{
            dialog.getDialogPane().setContent(fxmlLoader.load());
        }catch(IOException e) {
            e.printStackTrace();
            return;
        }
        dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
        dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
        Optional<ButtonType> result = dialog.showAndWait();

        DialogController controller = fxmlLoader.getController();

        if(result.isPresent() && result.get() == ButtonType.OK){
            boolean results = controller.processResults();

        }else{
            System.out.println("Cancelled");
            return;
        }
    }

}
...