JavaFX 应用程序:在窗格之间切换不会加载设置的内容
JavaFX App: Switching between Panes won't load set content
我有带侧面菜单栏的桌面应用程序。主要 window 是 BorderPane
,InsertLeft
包含 VBox
。我设置 Hbox buttons
及其行为,然后将它们一一添加到 VBox
。 InsertCenter
只有 Pane
有很多元素。
我为每个 GUI 布局创建了 3 个 fxml 文件。
- sample.fxml - BorderPane: InsertLeft->Menu(VBox), InsertCenter->Empty Pane
- tab1_content.fxml - 充满进度条、标签和按钮的窗格
- tab2_content.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.class
start()
方法:
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();
});
我有带侧面菜单栏的桌面应用程序。主要 window 是 BorderPane
,InsertLeft
包含 VBox
。我设置 Hbox buttons
及其行为,然后将它们一一添加到 VBox
。 InsertCenter
只有 Pane
有很多元素。
我为每个 GUI 布局创建了 3 个 fxml 文件。
- sample.fxml - BorderPane: InsertLeft->Menu(VBox), InsertCenter->Empty Pane
- tab1_content.fxml - 充满进度条、标签和按钮的窗格
- tab2_content.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.class
start()
方法:
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();
});