从另一个控制器设置 borderpane 的中心 class

Setting center of borderpane form another controller class

我将 Borderpane 作为 homePane(它的 fxml 文件是 home.fxml)。首先,在 homePane 的中心我有一个 HBox,其中包含一个名为 delivery 的按钮。当用户单击此按钮时,homePane 的中心切换为显示客户列表。 有效!

homeController代码:

public class HomeController {

@FXML
private BorderPane homePane; 

@FXML
public void deliveryBtnClicked (){
    FXMLLoader loader = new FXMLLoader(getClass().getResource("/fragments/customers.fxml"));
    homePane.getChildren().remove(homePane.getCenter());
    try {
        Pane pane = loader.load();
        homePane.setCenter(pane);

    }catch (IOException e){
        System.out.println(e.getMessage());
    }
}

public BorderPane getHomePane() {
    return homePane;
}
}

新窗格包含 table 个客户和一个名为订单的按钮。它有自己的控制器:

public class CustomerController {
    
    @FXML
    public void loadOrderView() throws IOException {
        Customer customer = customerTable.getSelectionModel().getSelectedItem();
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("/home.fxml"));
        loader.load();

        HomeController homeController =  loader.getController();
        BorderPane homePane = homeController.getHomePane();


        homePane.getChildren().remove(homePane.getCenter());
        System.out.println(homePane.getCenter());
        FXMLLoader loader1 = new FXMLLoader(getClass().getResource("/fragments/order.fxml"));
        Pane orderView = loader1.load();
        homePane.setCenter(orderView);
        System.out.println(homePane.getCenter());
     }
} 

当用户点击订单按钮时​​,订单视图必须加载到 homePane 的中央。 打印命令显示删除后的 homePane 中心为空,然后加载后获得新视图。

null
AnchorPane@755b1503

但在 运行 程序中它不显示顺序视图。我已将按钮顺序的 onAction 设置为 loadOrderView。这绝对不是问题。

正如 James 所指出的,您不应该再次加载 home fxml。

改为:

  1. 在控制器和 fxml 中定义 orderButton(通过 fx:id@FXML 注入)。

  2. 使用场景图找到主窗格,以便您可以替换它的中心。

public class CustomerController {
    
    @FXML Button orderButton
    
    @FXML
    public void loadOrderView() throws IOException {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/fragments/order.fxml"));
        Pane orderView = loader.load();
        BorderPane homePane = (BorderPane) orderButton.getScene().getRoot();
        homePane.setCenter(orderView);
     }
} 

小框架中的替代方法定义在以下问题的答案中:

  • Loading new fxml in the same scene

您可以导航场景图,如 所示,这对于小型应用程序来说是一个非常好的解决方案。

对于较大的应用程序,您可能需要在更改能力方面具有更大的灵活性,例如如何管理布局,请考虑使用 MVC/MVVM 类型的方法。从维持当前视图状态的模型 class 开始:

public class ViewModel {

    public enum View {
        CUSTOMER(ViewModel.class.getResource("/fragments/customer.fxml")),
        ORDER(ViewModel.class.getResource("/fragments/order.fxml"));

        private final URL resource ;

        private View(URL resource) {
            this.resource = resource ;
        }

        public URL getResource() {
            return resource ;
        }
    }

    private final ObjectProperty<View> currentView = new SimpleObjectProperty<>();

    public ObjectProperty<View> currentViewProperty() {
        return currentView ;
    }

    public final View getCurrentView() {
        return currentViewProperty().get();
    }

    public final void setCurrentView(View currentView) {
        currentViewProperty().set(currentView);
    }
}

让你的控制器实现一个接口,允许它们引用 ViewModel:

public interface ViewModelObserver {
    public void setViewModel(ViewModel model);
}

你的 HomeController,假设它被加载一次并且主页始终显示,可以创建 ViewModel 的单个实例,观察它,更新边框窗格,并传递实例加载到其他控制器时:

public class HomeController  {

    private final ViewModel viewModel = new ViewModel();

    @FXML
    private BorderPane homePane; 
    
    @FMXL
    public void initialize() {
        viewModel.currentViewProperty().addListener((obs, oldView, newView) -> {
            try {
                FXMLLoader loader = new FXMLLoader(newView.getResource());
                Parent root = loader.load();
                Object controller = loader.getController();
                if (controller instanceof ViewModelObserver) {
                    ((ViewModelObserver)controller).setViewModel(viewModel);
                }
                homePane.setCenter(root);
            } catch (Exception exc) {
                exc.printStackTrace();
            }
        });
    }
    
    @FXML
    public void deliveryBtnClicked (){
        viewModel.setCurrentView(ViewModel.View.CUSTOMER);
    }


}

然后为 FXML“片段”实现控制器 ViewModelObserver。他们可以简单地更新当前视图,家庭控制器将加载并显示它:

public class CustomerController implements ViewModelObserver {

    private ViewModel viewModel ;

    @Override
    public void setViewModel(ViewModel viewModel) {
        this.viewModel = viewModel ;
    }
    
    @FXML
    public void loadOrderView() throws IOException {
        viewModel.setCurrentView(ViewModel.View.ORDER);
     }
} 

您可能需要在视图模型中包含其他属性,例如授予对 Customer 实例等的访问权