从其控制器JavaFX-MVC外部访问节点

从其控制器JavaFX-MVC外部访问节点,javafx,controller,Javafx,Controller,我正在构建一个天气应用程序。有2个场景,其中包含2个控制器文件。一个是主屏幕,另一个是“设置”。主FXML包含一个标签,如果用户不想看到额外的信息,则必须在设置页面中打开/关闭该标签。我的问题是,如果可能的话,如何从设置页面的控制器类设置可见标签。 感谢您的帮助我假设只有当用户单击主屏幕上的按钮时,设置场景才会出现。我最近不得不在代码中处理同样的情况。下面是一个处理这种情况的很好的教程: 1.)在MainScene控制器中,您将引用main类并调用其函数以弹出场景设置 2.)在主类中,您将有一

我正在构建一个天气应用程序。有2个场景,其中包含2个控制器文件。一个是主屏幕,另一个是“设置”。主FXML包含一个标签,如果用户不想看到额外的信息,则必须在设置页面中打开/关闭该标签。我的问题是,如果可能的话,如何从设置页面的控制器类设置可见标签。
感谢您的帮助

我假设只有当用户单击主屏幕上的按钮时,设置场景才会出现。我最近不得不在代码中处理同样的情况。下面是一个处理这种情况的很好的教程:

1.)在MainScene控制器中,您将引用main类并调用其函数以弹出场景设置

2.)在主类中,您将有一个弹出设置场景的函数

3.)关闭设置场景后,它将通过主类将值传递回主场景控制器,并根据返回的值设置标签

1.)主场景的主控制器将具有对主类的引用和通过主类调用设置场景的函数

public class MainController {

@FXML
private Label label;
@FXML
private Button Settings;


// Reference to the main application
private MainApp mainApp;

/**
 * The constructor.
 * The constructor is called before the initialize() method.
 */
public MainController() {
}

/*Tie this function to your button that pops up Settings */

private void handleSettingsButton() { 

      /* Here you call a function in the main class and pass the label
       * to the settings scene controller */

        boolean show = mainApp.showSettingsScene(label);
        if (show) {
                label.isVisible("True");
         }
        else {
                label.isVisible("False");
        }
}

/**
 * Is called by the main application to give a reference back to itself.
 * 
 * @param mainApp
 */
public void setMainApp(MainApp mainApp) {
    this.mainApp = mainApp;


}
}
2.)在主类中(不要与主场景混淆),加载主场景并调用setMainApp函数,为控制器返回主类的引用

    public class MainApp extends Application {

private Stage primaryStage;
private BorderPane rootLayout;

@Override
public void start(Stage primaryStage) {
    this.primaryStage = primaryStage;
    this.primaryStage.setTitle("Main");

 /*Right when the app is loaded the MainScene shows up.*/

    try {
        // Load the root layout from the fxml file
        FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("view/MainScene.fxml"));
 /* Get a reference to the controller instance of the main Scene */
mainSceneController = loader.getController();
    /*Allow the controller to talk to the main class */
    mainSceneController.setMainApp(this);
        rootLayout = (BorderPane) loader.load();
        Scene scene = new Scene(rootLayout);
        primaryStage.setScene(scene);
        primaryStage.show();
    } catch (IOException e) {
        // Exception gets thrown if the fxml file could not be loaded
        e.printStackTrace();
    }


}

/**
 * Returns the main stage.
 * @return
 */
public Stage getPrimaryStage() {
    return primaryStage;
}

/*This function referenced in your main controller will show the Settings
 *Scene and wait to see what the user has selected for the visible or not 
 *visible selection.  We need to pass the label to it as well, so we
 *accurately load the Settings Scene with the current state of the label
 */

public boolean showSettingsScene(Label label) {
    try {
      // Load the fxml file and create a new stage for the popup
    FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("view/SettingsScene.fxml"));
settingsSceneController = loader.getController();

    /* Here we send the label to the controller instance of the Settings
     * Scene */

    controller.setLabel(label);
    AnchorPane page = (AnchorPane) loader.load();
    Stage dialogStage = new Stage();
    dialogStage.setTitle("Settings");
    dialogStage.initModality(Modality.WINDOW_MODAL);
    dialogStage.initOwner(primaryStage);
    Scene scene = new Scene(page);
    dialogStage.setScene(scene);

/* Show the dialog and wait until the user closes it*/
dialogStage.showAndWait();

 /*Return the value that the user has selected for visible or not */
return controller.isShowOrHide();

   } catch (IOException e) {
// Exception gets thrown if the fxml file could not be loaded
e.printStackTrace();
     return false;
   }
 }

public static void main(String[] args) {
    launch(args);
}
}
3.)场景控制器的设置如下所示:

import...
public class SettingsSceneController{

@FXML  private ComboBox showOrHide;

private Stage dialogStage;
private Boolean show = false;
private Label label;

/**
 * Initializes the controller class. This method is automatically called
 * after the fxml file has been loaded.  
 */
@FXML
private void initialize() {
     ;I don't know what you have, but if you use a Combobox...
     showOrHide.getItems().addAll(
          "Show",
          "Hide",);

}

/**
 * Sets the stage of this dialog.
 * @param dialogStage
 */
public void setDialogStage(Stage dialogStage) {
    this.dialogStage = dialogStage;
}

/*The label that was passed from Main Scene Controller to Main Class to 
 * here is now used in the function to update the Combobox with the
 * current status of the label */
public void setLabel(Label label) {
    this.label = label;
     if(label.isVisible){
          showOrHide.setValue("Show");
          show = true;
     }
     else{
          showOrHide.setValue("Hide"); 
          show = false;
     }
}



/**
 * Returns true if the user clicked OK, false otherwise.
 * @return
 */
public boolean isShowOrHide() {
    return show;
}

/**
 * Called when the user clicks ok. Attach this in Scene Builder,to the OK,
 * Enter or Apply or whatever you called it button of the Settings Scene 
 * It will reflect any change made to the combobox.
 */
@FXML
private void handleOk() {
    if (showOrHide.getValue().toString() == "Show") {
        show= true;
     }
    else{
        show = false;
     }
        dialogStage.close();

}

/**
 * Called when the user clicks cancel if you have a cancel button.
 */
@FXML
private void handleCancel() {
    dialogStage.close();
}


}
}

我从教程中获取了大部分代码,并根据您的解决方案对其进行了定制。它有点像是跳转到三个类,但是如果你稍微考虑一下,你可以看到它们是如何使用主类在控制器之间进行通信的。我没有对此进行测试,但它应该非常接近您的需要。

我假设只有当用户单击主屏幕上的按钮时,设置场景才会出现。我最近不得不在代码中处理同样的情况。下面是一个处理这种情况的很好的教程:

1.)在MainScene控制器中,您将引用main类并调用其函数以弹出场景设置

2.)在主类中,您将有一个弹出设置场景的函数

3.)关闭设置场景后,它将通过主类将值传递回主场景控制器,并根据返回的值设置标签

1.)主场景的主控制器将具有对主类的引用和通过主类调用设置场景的函数

public class MainController {

@FXML
private Label label;
@FXML
private Button Settings;


// Reference to the main application
private MainApp mainApp;

/**
 * The constructor.
 * The constructor is called before the initialize() method.
 */
public MainController() {
}

/*Tie this function to your button that pops up Settings */

private void handleSettingsButton() { 

      /* Here you call a function in the main class and pass the label
       * to the settings scene controller */

        boolean show = mainApp.showSettingsScene(label);
        if (show) {
                label.isVisible("True");
         }
        else {
                label.isVisible("False");
        }
}

/**
 * Is called by the main application to give a reference back to itself.
 * 
 * @param mainApp
 */
public void setMainApp(MainApp mainApp) {
    this.mainApp = mainApp;


}
}
2.)在主类中(不要与主场景混淆),加载主场景并调用setMainApp函数,为控制器返回主类的引用

    public class MainApp extends Application {

private Stage primaryStage;
private BorderPane rootLayout;

@Override
public void start(Stage primaryStage) {
    this.primaryStage = primaryStage;
    this.primaryStage.setTitle("Main");

 /*Right when the app is loaded the MainScene shows up.*/

    try {
        // Load the root layout from the fxml file
        FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("view/MainScene.fxml"));
 /* Get a reference to the controller instance of the main Scene */
mainSceneController = loader.getController();
    /*Allow the controller to talk to the main class */
    mainSceneController.setMainApp(this);
        rootLayout = (BorderPane) loader.load();
        Scene scene = new Scene(rootLayout);
        primaryStage.setScene(scene);
        primaryStage.show();
    } catch (IOException e) {
        // Exception gets thrown if the fxml file could not be loaded
        e.printStackTrace();
    }


}

/**
 * Returns the main stage.
 * @return
 */
public Stage getPrimaryStage() {
    return primaryStage;
}

/*This function referenced in your main controller will show the Settings
 *Scene and wait to see what the user has selected for the visible or not 
 *visible selection.  We need to pass the label to it as well, so we
 *accurately load the Settings Scene with the current state of the label
 */

public boolean showSettingsScene(Label label) {
    try {
      // Load the fxml file and create a new stage for the popup
    FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("view/SettingsScene.fxml"));
settingsSceneController = loader.getController();

    /* Here we send the label to the controller instance of the Settings
     * Scene */

    controller.setLabel(label);
    AnchorPane page = (AnchorPane) loader.load();
    Stage dialogStage = new Stage();
    dialogStage.setTitle("Settings");
    dialogStage.initModality(Modality.WINDOW_MODAL);
    dialogStage.initOwner(primaryStage);
    Scene scene = new Scene(page);
    dialogStage.setScene(scene);

/* Show the dialog and wait until the user closes it*/
dialogStage.showAndWait();

 /*Return the value that the user has selected for visible or not */
return controller.isShowOrHide();

   } catch (IOException e) {
// Exception gets thrown if the fxml file could not be loaded
e.printStackTrace();
     return false;
   }
 }

public static void main(String[] args) {
    launch(args);
}
}
3.)场景控制器的设置如下所示:

import...
public class SettingsSceneController{

@FXML  private ComboBox showOrHide;

private Stage dialogStage;
private Boolean show = false;
private Label label;

/**
 * Initializes the controller class. This method is automatically called
 * after the fxml file has been loaded.  
 */
@FXML
private void initialize() {
     ;I don't know what you have, but if you use a Combobox...
     showOrHide.getItems().addAll(
          "Show",
          "Hide",);

}

/**
 * Sets the stage of this dialog.
 * @param dialogStage
 */
public void setDialogStage(Stage dialogStage) {
    this.dialogStage = dialogStage;
}

/*The label that was passed from Main Scene Controller to Main Class to 
 * here is now used in the function to update the Combobox with the
 * current status of the label */
public void setLabel(Label label) {
    this.label = label;
     if(label.isVisible){
          showOrHide.setValue("Show");
          show = true;
     }
     else{
          showOrHide.setValue("Hide"); 
          show = false;
     }
}



/**
 * Returns true if the user clicked OK, false otherwise.
 * @return
 */
public boolean isShowOrHide() {
    return show;
}

/**
 * Called when the user clicks ok. Attach this in Scene Builder,to the OK,
 * Enter or Apply or whatever you called it button of the Settings Scene 
 * It will reflect any change made to the combobox.
 */
@FXML
private void handleOk() {
    if (showOrHide.getValue().toString() == "Show") {
        show= true;
     }
    else{
        show = false;
     }
        dialogStage.close();

}

/**
 * Called when the user clicks cancel if you have a cancel button.
 */
@FXML
private void handleCancel() {
    dialogStage.close();
}


}
}

我从教程中获取了大部分代码,并根据您的解决方案对其进行了定制。它有点像是跳转到三个类,但是如果你稍微考虑一下,你可以看到它们是如何使用主类在控制器之间进行通信的。我没有对此进行测试,但它应该与您需要的非常接近。

这两个FXML文件之间的关系是什么?(例如,何时显示“设置”页面?)您不应在控制器外部公开节点,而应在控制器中放入可调用的功能以控制UI的状态,或者(更好)在两个控制器之间共享模型并修改/观察其中的数据。你需要更详细地展示你是如何为任何人(或者,至少,对我来说:其他人可能更具洞察力…)设置的,以便能够提供帮助。James D是对的。如果将此可见性的更改直接存储在控制器中,则很难从其他来源(例如设置文件)获取数据。您最好将设置存储在模型中。两个FXML文件之间的关系是什么?(例如,何时显示“设置”页面?)您不应在控制器外部公开节点,而应在控制器中放入可调用的功能以控制UI的状态,或者(更好)在两个控制器之间共享模型并修改/观察其中的数据。你需要更详细地展示你是如何为任何人(或者,至少,对我来说:其他人可能更具洞察力…)设置的,以便能够提供帮助。James D是对的。如果将此可见性的更改直接存储在控制器中,则很难从其他来源(例如设置文件)获取数据。您最好将设置存储在模型中。