JavaFX: невозможно обновить GridPane динамически

Хорошо, вот сложный вопрос, который сводит меня с ума уже пару дней...

У меня есть два этапа: этап администрирования пользователей (контроллер: UsersAdminController.java) этап администрирования пользователейсо следующей иерархией элементов : Иерархия элементов на этапе администрирования пользователяи этапе добавления пользователя: (Контроллер: AddUserController.java) Этап добавления пользователя

В качестве дочернего элемента в панель прокрутки верхней панели привязки на этапе администрирования пользователей (см. иерархию элементов) я добавляю во время инициализации UsersAdminController панель GridPane, предназначенную для вывода списка пользователей системы, как описано здесь Когда я добавляю пользователя через экран добавления пользователя, я пытаюсь обновить GridPane на этапе администрирования пользователей путем получения ссылки на UsersAdminController и последующего вызова метода, обновляющего его содержимое. Однако GridPane НЕ МОЖЕТ БЫТЬ ОБНОВЛЕН. (Похоже, я не могу выполнить какое-либо действие, связанное с графическим интерфейсом, ни с панелью прокрутки, ни с панелью сетки, хотя мне удается получить ссылку на соответствующий контроллер.

Код: вот как (правильно) инициализируется панель сетки с помощью метода инициализации UsersAdminController:

public void initialize(URL location, ResourceBundle resources) {
    gridPaneNonSudoUsers = new GridPane();
    gridPaneNonSudoUsers.setId("gridPaneNonSudoUsers");
    // setting column widths for the grid pane
    gridPaneNonSudoUsers.getColumnConstraints().add(new ColumnConstraints(145));
    gridPaneNonSudoUsers.getColumnConstraints().add(new ColumnConstraints(85));
    gridPaneNonSudoUsers.getColumnConstraints().add(new ColumnConstraints(210));
    gridPaneNonSudoUsers.getColumnConstraints().add(new ColumnConstraints(150));

    srcPaneUsers.setContent(gridPaneNonSudoUsers);
    gridPaneNonSudoUsers.setAlignment(Pos.CENTER);

    displayNonSudoUsers(getSystemNonSudoUsers());
}

Вот displayNonSudoUsers, который заполняет GridPane.

public void displayNonSudoUsers (ArrayList nonSudoUsers) {

    // adding some space between lines
    gridPaneNonSudoUsers.setVgap(15);

    Label lblHeaderUsername = new Label("Username");
    lblHeaderUsername.setPadding(new Insets(0, 0, 0, 20));
    lblHeaderUsername.setId("lblHeaderUsername");
    Label lblHeaderIsProtected = new Label("Προστασία");
    lblHeaderIsProtected.setId("lblHeaderIsProtected");
    GridPane.setHalignment(lblHeaderUsername, HPos.LEFT);
    GridPane.setHalignment(lblHeaderIsProtected, HPos.CENTER);

    // adding header elements to GridPane
    GridPane.setColumnIndex(lblHeaderUsername, 0);
    GridPane.setRowIndex(lblHeaderUsername, 0);
    GridPane.setColumnIndex(lblHeaderIsProtected, 1);
    GridPane.setRowIndex(lblHeaderIsProtected, 0);
    gridPaneNonSudoUsers.getChildren().addAll(lblHeaderUsername,
            lblHeaderIsProtected);

    // adding users
    int i = 1; // starting from 2nd row given that 1st row belongs to
                // headers
    for (NonSudoUser nsuser : nonSudoUsers) {

        /**** Label ****/
        String username = nsuser.getUsername();
        Label lblUsername = new Label(username);
        lblUsername.setPadding(new Insets(5, 0, 5, 20));
        lblUsername.setId("lbl" + username);
        GridPane.setHalignment(lblUsername, HPos.LEFT);

        /**** Image ****/
        boolean isProtected = nsuser.isProtected();
        ImageView imgIsProtected = new ImageView(
                isProtected ? "/resources/media/notOK.png"
                        : "/resources/media/ok.png");
        imgIsProtected.setFitHeight(20);
        imgIsProtected.setFitWidth(20);
        imgIsProtected.setId("img" + username);
        GridPane.setHalignment(imgIsProtected, HPos.CENTER);

        /**** Button for protection ****/
        Button btnSetProtected = new Button("Ενεργοποίηση Προστασίας");
        btnSetProtected.setId("btnProt" + username);
        btnSetProtected.setDisable(!isProtected);
        GridPane.setHalignment(btnSetProtected, HPos.CENTER);

        /**** Button for deletion ****/
        Button btnDeleteUser = new Button("Διαγραφή Χρήστη");
        btnDeleteUser.setId("btnDel" + username);
        GridPane.setHalignment(btnDeleteUser, HPos.CENTER);
        // delete user in a seperate thread
        btnDeleteUser.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                Action response = Dialogs
                        .create()
                        .owner(root)
                        .title("ΔΙΑΓΡΑΦΗ ΧΡΗΣΤΗ")
                        .masthead(
                                "ΠΡΟΣΟΧΗ! ΠΡΟΚΕΙΤΑΙ ΝΑ ΔΙΑΓΡΑΨΕΤΕ ΤΟ ΧΡΗΣΤΗ: "
                                        + username)
                        .message("ΕΙΣΑΣΤΕ ΣΙΓΟΥΡΟΙ?")
                        .actions(Dialog.ACTION_OK, Dialog.ACTION_CANCEL)
                        .showConfirm();
                if (response == Dialog.ACTION_CANCEL)
                    return;
                Task<Void> task = new Task<Void>() {
                    @Override
                    public Void call() throws Exception {
                        String username = ((Button) event.getSource())
                                .getId();
                        LinuxCommand lc = new LinuxCommand("userdel", "-r",
                                username);
                        lc.execute();
                        return null;
                    }
                };
                task.setOnFailed(new EventHandler<WorkerStateEvent>() {
                    public void handle(WorkerStateEvent t) {
                        System.out.println("FAILURE deleting  " + username);
                    }
                });
                task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
                    public void handle(WorkerStateEvent t) {
                        Platform.runLater(new Task<Void>() {
                            @Override
                            public Void call() throws Exception {
                                ArrayList<Node> toErase = new ArrayList<Node>();
                                for (Node nodeToErase : gridPaneNonSudoUsers
                                        .getChildren()) {
                                    if (nodeToErase.getId().endsWith(
                                            username))
                                        toErase.add(nodeToErase);
                                }
                                gridPaneNonSudoUsers.getChildren()
                                        .removeAll(toErase);
                                // rearrangeGridPane(); - TBD
                                return null;
                            }
                        });
                    }
                });
                executorUserDeletion.submit(task);
            }
        });

        // adding user-related gui items
        GridPane.setColumnIndex(lblUsername, 0);
        GridPane.setRowIndex(lblUsername, i);

        GridPane.setColumnIndex(imgIsProtected, 1);
        GridPane.setRowIndex(imgIsProtected, i);

        GridPane.setColumnIndex(btnSetProtected, 2);
        GridPane.setRowIndex(btnSetProtected, i);

        GridPane.setColumnIndex(btnDeleteUser, 3);
        GridPane.setRowIndex(btnDeleteUser, i);

        gridPaneNonSudoUsers.getChildren().addAll(lblUsername,
                imgIsProtected, btnSetProtected, btnDeleteUser);
        i++;
    }
}

А вот метод, который вызывается по действию КНОПКИ ДОБАВИТЬ ПОЛЬЗОВАТЕЛЯ стадии добавления пользователя

    public void createUser() throws UserCreationException {

        String username, password1, password2;
        username = txtUsername.getText();
        password1 = txtPassword1.getText();
        password2 = txtPassword2.getText();
        LinuxCommand lc = new LinuxCommand(System.getProperty("user.dir")+"/adduser.sh", username, password2);
    Task<Void> task = new Task<Void>() {
        @Override
        public Void call() throws Exception {
            lc.execute();
            return null;
        }
    };
    task.setOnFailed(new EventHandler<WorkerStateEvent>() {
        public void handle(WorkerStateEvent t) {
            System.out.println("FAILURE creating  " + username);
            System.out.println("exit code: " + lc.getExitCode());
        }
    });
    task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
        @Override
        public void handle(WorkerStateEvent t) {
            if (lc.getExitCode() != 0)
                // show dialog
                return;
            System.out.println("Successfully created new user:  " + username + " With exit code: " + lc.getExitCode());
            Platform.runLater(new Task<Void>() {
                @Override
                public Void call() throws Exception {                       
                    uac.addUser();          
                    return null;

                }
            });
        }
    });

    executorUserCreation.submit(task);

где uac — это ссылка на UserAdministrationController.java, а adduser — на метод, который пытается обновить GridPane, ПОВТОРНО ВЫЗЫВАЯ displayNonSudoUsers(). Я также экспериментировал, пытаясь делать различные вещи на панели прокрутки / панели сетки на этапе администрирования пользователей (например, удаление метки и т. д.), но НАДА!

Спасибо.

РЕДАКТИРОВАТЬ: И именно так я получаю ссылку на контроллер администратора пользователя (что, вероятно, правильно, потому что, например, мне удается sysout некоторые из его идентификаторов элементов и т. д.) в методе инициализации AddUserController.java

public void initialize(URL location, ResourceBundle resources) {
    // need to access UsersAdminController to update users list 
    // when adding user
    URL loc = getClass().getResource("/resources/fxml/UsersAdminPane.fxml"); // the UsersAdmin fxml 
    FXMLLoader fxmlLoader = new FXMLLoader();
    fxmlLoader.setLocation(loc);
    fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
    try {
        Parent root = (Parent) fxmlLoader.load(loc.openStream());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    uac = (UsersAdminController)fxmlLoader.getController();
}

person pkaramol    schedule 20.10.2014    source источник
comment
Как вы получаете ссылку на UserAdministrationController в AddUserController?   -  person James_D    schedule 20.10.2014
comment
Ответил в редакции   -  person pkaramol    schedule 20.10.2014
comment
Вы загружаете новую UsersAdminPane в AddUserController? Разве такой уже не существует?   -  person James_D    schedule 20.10.2014
comment
Ваш код довольно сложен, и будет трудно понять, что происходит. Я бы порекомендовал создать новое приложение полностью с нуля, в котором один контроллер вызывает другой контроллер для обновления связанного с ним представления. Кроме того, сделайте это настолько простым, насколько это возможно. Когда у вас есть техника, позволяющая заставить это работать, вы можете попытаться исправить свое реальное приложение.   -  person James_D    schedule 20.10.2014
comment
Если я не сделаю Parent root = (Parent) fxmlLoader.load(loc.openStream());, я получу исключение NullPointerException, когда попытаюсь получить доступ к uac.   -  person pkaramol    schedule 20.10.2014
comment
@пкарамол. Если перед переменной gridPaneNonSudoUsers стоит @FXML, не инициализируйте ее. А именно удалить строку gridPaneNonSudoUsers = new GridPane();   -  person Uluk Biy    schedule 20.10.2014
comment
@pkaramol Дело в том, что вам нужна ссылка на существующий контроллер: тот, который управляет отображаемым представлением. Прямо сейчас вы создаете совершенно новую пару представление/контроллер и получаете доступ к этому контроллеру. Это представление даже нигде не отображается, поэтому вы обновляете представление, которое никогда не увидите.   -  person James_D    schedule 20.10.2014


Ответы (1)


В этом коде:

public void initialize(URL location, ResourceBundle resources) {
    // need to access UsersAdminController to update users list 
    // when adding user
    URL loc = getClass().getResource("/resources/fxml/UsersAdminPane.fxml"); // the UsersAdmin fxml 
    FXMLLoader fxmlLoader = new FXMLLoader();
    fxmlLoader.setLocation(loc);
    fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
    try {
        Parent root = (Parent) fxmlLoader.load(loc.openStream());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    uac = (UsersAdminController)fxmlLoader.getController();
}

вы загружаете новую копию UsersAdminPane.fxml, получаете ссылку на ее контроллер... а затем отбрасываете представление (root) и ничего с ним не делаете. Поскольку это представление никогда не отображается, вызов методов контроллера никогда не приведет к каким-либо обновлениям, которые вы увидите.

Вам нужно получить ссылку на контроллер, связанный с существующим представлением, и вызвать для него методы вместо создания нового контроллера. Предполагая, что вы загружаете fxml-файл AddUser из UsersAdminController, вы можете получить AddUserController из FXMLLoader и передать ссылку на UsersAdminController, определив метод setUsersAdminController(...) в файле AddUserController.

person James_D    schedule 20.10.2014
comment
Вы, наверное, правы, я этого не понимал. Предполагая, что у меня есть ссылка на этап, связанный с существующим представлением UsersAdminPane.fxml, есть ли у вас какие-либо предложения о том, как получить доступ к соответствующему контроллеру? Большое спасибо за ваше время. - person pkaramol; 20.10.2014
comment
Смотрите обновление. Также см. stackoverflow.com/questions/14187963/ - person James_D; 20.10.2014