Загрузить существующий FXML из родительской панели

Я работаю над приложением, в котором есть панель mainPane, в которой хранится несколько панелей меньшего размера.

Сначала я начал с добавления прослушивателя для каждой нажатой кнопки, после нажатия определенной кнопки должна появиться коррелирующая панель. Это работало нормально, пока я не понял, что добавляю новый экземпляр панели с каждым нажатием кнопки и в конечном итоге теряю прогресс на изначально выбранных панелях где-то в фоновом режиме.

С тех пор я пытался загрузить существующие панели из метода getChildren() основной родительской панели, однако у меня не было большого успеха в этом. Я пытался найти решение повсюду здесь, но я не смог найти ничего, относящегося к моей проблеме, или, если я что-то заметил, я, возможно, не понял этого, если вообще не понял.

Мой вопрос заключается в следующем: если я могу добавить дочернюю панель в родительскую панель, чтобы начать с использования mainPane.getChildren().add(root), я затем добавил простой логический переключатель, который будет установлен, если панель добавлена, и если панель этого типа уже добавлена ​​- попытайтесь показать это с помощью mainPane.getChildren().get(intArray[0]).toFront();. Однако при добавлении этого ко всем другим обработчикам кнопок метод .toFront(), похоже, показывает панели в том порядке, в котором они были добавлены.

Итак, если я сначала нажму кнопки trackerButton, progressButton, communityButton, blockerButton - и я нажму trackerButton 4 раза, панель отобразит -> trackerPane, progressPane, communityPane, blockerPane, а затем снова переберу их, если я продолжаю нажимать.

Есть ли более чистый способ реализовать это, чтобы, если я нажимаю кнопку несколько раз, она загружала только коррелирующую панель, но не добавляла новый экземпляр с mainPane.getChildren().add(root); ?

Любая помощь будет принята с благодарностью, я вставил свой код ниже:

public class MainController {

private Client client;

@FXML
TrackerPaneController trackerPaneController;

@FXML
ProgressController progressController;

@FXML
CommunityController communityController;

@FXML
BlockerController blockerController;

@FXML
SettingsController settingsController;

@FXML
Label userLabel;

@FXML
Button logoutButton;

@FXML
Button trackerButton;

@FXML
Button progressButton;

@FXML 
Button communityButton;

@FXML
Button blockerButton;

@FXML
Button settingsButton;

@FXML
Pane mainPane;

Integer[] intArray = new Integer[4];
final Boolean[] booleanArray = {true,true,true,true,true};


@FXML
private void handlePanelButtonAction(ActionEvent event) throws IOException {


    if(event.getSource() == trackerButton) {

        if(booleanArray[0]) {


        FXMLLoader trackerLoader = new FXMLLoader(getClass().getResource("/view/TrackerPane.fxml"));
        Parent root = (Parent) trackerLoader.load();

        trackerPaneController = trackerLoader.getController();
        trackerPaneController.setClient(client);
        mainPane.getChildren().add(root);

        intArray[0] = mainPane.getChildren().indexOf(root);

        }

        mainPane.getChildren().get(intArray[0]).toFront();
        booleanArray[0]=false;

    } else if(event.getSource() == progressButton) {

        if(booleanArray[1]) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/ProgressPane.fxml"));
        Parent root = (Parent) loader.load();
        progressController = loader.getController();
        progressController.setClient(client);
        mainPane.getChildren().add(root);
        intArray[1] = mainPane.getChildren().indexOf(root);

        }

        mainPane.getChildren().get(intArray[1]).toFront();
        booleanArray[1]=false;

    } else if(event.getSource() == communityButton) {

        if(booleanArray[2]) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/CommunityPane.fxml"));
        Parent root = (Parent) loader.load();
        communityController = loader.getController();
        communityController.setClient(client);
        mainPane.getChildren().add(root);
        intArray[2] = mainPane.getChildren().indexOf(root);

        }

        mainPane.getChildren().get(intArray[2]).toFront();
        booleanArray[2]=false;

    } else if(event.getSource() == blockerButton) {

        if(booleanArray[3]) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/BlockerPane.fxml"));
        Parent root = (Parent) loader.load();
        blockerController = loader.getController();
        blockerController.setClient(client);
        mainPane.getChildren().add(root);
        intArray[3] = mainPane.getChildren().indexOf(root);

        }

        mainPane.getChildren().get(intArray[3]).toFront();
        booleanArray[3]=false;

    } else if(event.getSource() == settingsButton) {

        if(booleanArray[4]) {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/SettingsPane.fxml"));
        Parent root = (Parent) loader.load();
        settingsController = loader.getController();
        settingsController.setClient(client);
        mainPane.getChildren().add(root);
        intArray[4] = mainPane.getChildren().indexOf(root);

        }

        mainPane.getChildren().get(intArray[4]).toFront();
        booleanArray[4]=false;

    } else if(event.getSource() == logoutButton) {

        client.logout();
        Platform.exit();
    }
}

}

Я чувствую, что решение очень простое для кого-то с нужным набором навыков, однако я все еще очень новичок в JavaFX, и любая помощь будет очень признательна.

С уважением


person MrRobot    schedule 09.08.2018    source источник


Ответы (1)


Проблема в том, что вызов toFront для Node приводит к перемещению узла в последний индекс родительского макета. Это приводит к тому, что в некоторых случаях некоторые индексы становятся неправильными. Чтобы исправить это, просто сохраните Node, а не индекс в вашей структуре данных.

Также обратите внимание, что в основном весь код загрузки выглядит одинаково, за исключением некоторых незначительных отличий. Вам следует избегать повторения кода, так как это усложняет его поддержку. Повторяющийся код можно легко удалить, добавив интерфейс к вашим контроллерам.

@FXML
private void handlePanelButtonAction(ActionEvent event) throws IOException {
    if(event.getSource() == trackerButton) {
        ...
    } else if(event.getSource() == progressButton) {
        ...
    } else if(event.getSource() == communityButton) {
        ...
    } else if(event.getSource() == blockerButton) {
        ...
    } else if(event.getSource() == settingsButton) {
        ...
    } else if(event.getSource() == logoutButton) {
        ...
    }
}

Не делай ничего подобного. Если логика совершенно другая, Button должен получить собственный метод обработчика событий. Даже если код достаточно похож, вам следует избегать проверки равенства ссылок источника с некоторыми из ваших узлов. Вместо этого вы можете хранить информацию в userData или properties файла Node.

Рекомендация

Реализуйте следующий интерфейс с вашими контроллерами:

public interface ClientContainer {
    void setClient(Client client);
}

Инициализируйте userData ваших кнопок с помощью URLs:

public class MainController {

    ...

    private void setResource(Node node, String resource) {
        URL url = getClass().getResource(resource);
        if (url == null) {
            throw new IllegalArgumentException("Resource not available: " + resource);
        }
        node.setUserData(url);
    }

    private static URL getResource(Object source) {
        return (URL) ((Node) source).getUserData();
    }

    @FXML
    private void initialize() {
        setResource(trackerButton, "/view/TrackerPane.fxml");
        setResource(progressButton, "/view/ProgressPane.fxml");
        setResource(communityButton, "/view/CommunityPane.fxml");
        setResource(blockerButton, "/view/BlockerPane.fxml");
        setResource(settingsButton, "/view/SettingsPane.fxml");
    }
}

Используйте эти данные и Map, чтобы избежать перезагрузки сцен. (Вам также необходимо переместить код для обработки клика logoutButton в другой метод.)

private final Map<URL, Node> loadedContent = new HashMap<>();

@FXML
private void handlePanelButtonAction(ActionEvent event) {
    loadedContent.computeIfAbsent(getResource(event.getSource()), url -> {
        try {
            FXMLLoader loader = new FXMLLoader(url);
            Node result = loader.load();
            ClientContainer controller = loader.getController();
            controller.setClient(client);
            mainPane.getChildren().add(result);
            return result;
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }).toFront();
}
person fabian    schedule 10.08.2018
comment
Просто хотел сказать спасибо, Фабиан. Я реализовал вышеизложенное, и теперь все работает как шарм. Всего наилучшего! - person MrRobot; 10.08.2018