JavaFX 应用程序:在窗格之间切换不会加载设置的内容

JavaFX App: Switching between Panes won't load set content

我有带侧面菜单栏的桌面应用程序。主要 window 是 BorderPaneInsertLeft 包含 VBox。我设置 Hbox buttons 及其行为,然后将它们一一添加到 VBoxInsertCenter 只有 Pane 有很多元素。

我为每个 GUI 布局创建了 3 个 fxml 文件。

这些 fxml 文件中的每一个都有其控制器。

我想在单击菜单按钮时在 sample.fxml 中切换 borderPane.center() 的内容。

我已经设法解决了一些问题,但主要问题是将数据加载到 .fxml 视图中。

因为我 运行 我的应用程序运行完美,每个 fxml 文件都有自己的 FXMLLoader,它将内容加载到主控制器内的 borderPane 中。

单击按钮时出现问题。它将切换窗格,但实际内容将重置为默认状态并且 Main.class 初始化被完全忽略。按钮侦听器和标签值未初始化。它只是空的 fxml 布局。 Main.class 中的每个变量,我想从 Tab1Controller 访问的内容都返回 NullPointerException - 甚至是方法。

每个控制器都扩展了 AbstractController,它包含 Main.class 在 start() 方法中初始化的实例。所以我应该可以随时访问每个 Main.class method/variable。

发布动图:

一些代码示例:

我的Main.classstart()方法:

    public Controller myController;

    @Override
        public void start(Stage primaryStage) throws Exception {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(getClass().getResource("/sample.fxml"));
            myController = new Controller();
            myController.setMainApp(this);
            loader.setController(myController);
            Parent root = loader.load();
            primaryStage.setTitle("Simple App");
            primaryStage.setScene(new Scene(root));
            primaryStage.show();

            <other stuff>
    }

public void setDefaultViewProperties(){
        currentScanningFileProperty = new SimpleStringProperty();
        myController.tab1Controller.actualPath.textProperty().bind(currentScanningFileProperty); //NullPointerException while called from Controller
        fileCounterProperty = new SimpleLongProperty();
        myController.tab1Controller.numOfScanned.textProperty().bind(fileCounterProperty.asString());
        maliciousFilesCounterProperty = new SimpleIntegerProperty();
        myController.tab1Controller.numOfMaliciousFiles.textProperty().bind(maliciousFilesCounterProperty.asString());

        myController.tab1Controller.fileChoiceBtn.setOnMouseClicked(event -> chooseFile());
        myController.tab1Controller.runScanBtn.setOnMouseClicked(event -> new Thread(() -> {
            try {
                resetValues();
                startFileWalking(chosenFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start());
    }

主控制器:

package sample;

import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

public class Controller extends AbstractController implements Initializable{
    public HBox sideMenu;
    public VBox mainMenu;
    public BorderPane borderPane;
    public Boolean isButton1Pressed = false;
    public Boolean isButton2Pressed = false;
    public static final String TAB_1 = "TAB-1";
    public static final String TAB_2 = "TAB-2";
    public Button malwareButton;
    public Button webShieldButton;
    public Tab1Controller tab1Controller;
    public Tab2Controller tab2Controller;


    @Override
    public void initialize(URL location, ResourceBundle resources) {

        createMenuButtons();
        setSideMenu();
        setMenuButtonsListeners();
    }

    private void setSideMenu(){
        mainMenu.getChildren().add(item(malwareButton));
        mainMenu.getChildren().add(item(webShieldButton));
        mainMenu.setStyle("-fx-background-color:#004D40");
    }

    private HBox item(Button menuButton){
        menuButton.setPrefSize(200, 50);
        menuButton.setStyle("-fx-background-color: transparent;");
        menuButton.setTextFill(Color.web("#E0F2F1"));
        menuButton.setPadding(Insets.EMPTY);
        sideMenu = new HBox(menuButton);
        return sideMenu;
    }

    public void setMenuButtonsListeners(){
        malwareButton.setOnMousePressed(event -> {
            setButtonStylePressed(malwareButton);
            setButtonStyleUnpressed(webShieldButton);
            isButton1Pressed = true;
            isButton2Pressed = false;
            loadTab1Content();
            main.setDefaultViewProperties();
        });

        webShieldButton.setOnMousePressed(event -> {
            setButtonStylePressed(webShieldButton);
            setButtonStyleUnpressed(malwareButton);
            isButton1Pressed = false;
            isButton2Pressed = true;
            loadTab2Content();
        });

        malwareButton.setOnMouseExited(event -> {
            if(!isButton1Pressed){
                setButtonStyleUnpressed(malwareButton);
            }
        });
        webShieldButton.setOnMouseExited(event -> {
            if(!isButton2Pressed){
                setButtonStyleUnpressed(webShieldButton);
            }
        });

        malwareButton.setOnMouseEntered(event -> setButtonStylePressed(malwareButton));
        webShieldButton.setOnMouseEntered(event -> setButtonStylePressed(webShieldButton));
    }

    public void setButtonStylePressed(Button btn){
        btn.setStyle("-fx-background-color: #E0F2F1");
        btn.setTextFill(Color.web("#004D40"));
    }

    public void setButtonStyleUnpressed(Button btn){
        btn.setStyle("-fx-background-color: transparent");
        btn.setTextFill(Color.web("#E0F2F1"));
    }

    private void loadTab1Content(){
        FXMLLoader tab1loader = new FXMLLoader();
        tab1loader.setLocation(getClass().getResource("/tab_1_content.fxml"));
        try {
            if (tab1Controller == null){
                tab1Controller = new Tab1Controller();
            }
            tab1loader.setController(tab1Controller);
            borderPane.setCenter(tab1loader.load());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void loadTab2Content(){
        FXMLLoader tab2loader = new FXMLLoader();
        tab2loader.setLocation(getClass().getResource("/tab_2_content.fxml"));
        try {
            if (tab2Controller == null){
                tab2Controller = new Tab2Controller();
            }
            tab2loader.setController(tab2Controller);
            borderPane.setCenter(tab2loader.load());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void createMenuButtons(){
        malwareButton = new Button();
        malwareButton.setText(TAB_1);
        webShieldButton = new Button();
        webShieldButton.setText(TAB_2);
    }
}

Tab1 控制器:

package sample;

import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;

/**
 * Created by admin on 5. 5. 2018.
 */
public class Tab1Controller extends AbstractController implements Initializable {


    public ProgressBar progressBar;
    public Button runScanBtn;
    public Button fileChoiceBtn;
    public Label chosenPath;
    public Label actualPath;
    public Label numOfMaliciousFiles;
    public Label hashValue;
    public Label scanFinishedMsg;
    public Label numOfScanned;
    public Button showFoundMalwareButton;



    @Override
    public void initialize(URL location, ResourceBundle resources) {


        runScanBtn.setDisable(true);
        scanFinishedMsg.setVisible(false);
        showFoundMalwareButton.setVisible(false);
        showFoundMalwareButton.setOnAction(event -> showPopupWindow());
    }

Update#1 - 单击按钮后通过 Main.class 更新 fxml 值

我终于成功 运行 应用程序,无一例外。我必须为名为 Tab1Controller 的窗格 fxml 布局本身创建下一个控制器。当我初始化Controller的时候,它瞬间在里面初始化了Tab1Controller。因此,当我想更改中心 BorderPane 标签时,我不得不调用 myController.tab1Controller.tabLabel.setText()

我不知道这是否是解决这个问题的好方法。 但现在我又回到了我的老问题。当我点击 TAB-1 时,它会加载内容,但值未初始化为默认状态。

例如,我实时更新了几个标签。我使用默认值将一些 SimpleProperties 绑定到其中。它以前工作过,但是因为我有三个控制器,所以它会第一次加载数据,但是当我单击 TAB-1 按钮时,它只会加载 fxml 内容,但不会设置这些标签。

所以我在 Main.class 中创建了 public 方法,每次从 Controller 切换到 TAB-1 时我都会调用它。

public void setDefaultViewProperties(){
    myController.tab1Controller.actualPath.textProperty().bind(currentScanningFileProperty);
    myController.tab1Controller.numOfScanned.textProperty().bind(fileCounterProperty.asString());
    myController.tab1Controller.numOfMaliciousFiles.textProperty().bind(maliciousFilesCounterProperty.asString());
}

但现在每次我点击 TAB-1 时我都会得到

java.lang.NullPointerException: Cannot bind to null

您可以制作两个窗格并使用 setVisible() 方法在它们之间切换

示例:

void btn1Clicked() {
   pane1.setVisible(true);
   pane2.setVisible(false);
}

void btn2Clicked() {
   pane1.setVisible(false);
   pane2.setVisible(true);
}

您可以使用 TabPane 来实现此行为: https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TabPane.html

已解决。我不确定如何,但 setDefaultViewProperties() 目前没有抛出 NullPointerException。我没有更改代码中的任何内容:

malwareButton.setOnMousePressed(event -> {
                setButtonStylePressed(malwareButton);
                setButtonStyleUnpressed(webShieldButton);
                isButton1Pressed = true;
                isButton2Pressed = false;
                loadTab1Content();
                main.setDefaultViewProperties();
            });