如何使用不同的FXML文件创建多个JavaFX控制器?

31

我一直在查阅博客和其他的stackoverflow问题,但是没有看到我的问题有一个直接的答案。我正在创建一个JavaFX GUI客户端,并且我希望我的菜单栏是一个FXML中的一个控制器,然后我希望内容区域是另外的FXML文件。登录界面将是一个FXML,登录后将是应用程序的主要内容,它将在一个FXML中。我该如何做呢?

我只是不想把我的登录、菜单栏和主要内容的所有代码都放在同一个文件中。这是我正在工作的图片:

enter image description here


3
一些使用了一些问题答案中的概念的示例代码 - jewelsea
这里有另一个例子:链接 - Dil
4个回答

36

通过使用自定义的Java类作为fx:root和fx:controller,将FXML用作组件:

http://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm

要实现这一点,需要在自定义Java类的构造函数中调用FXMLLoader,它将加载您的FXML文件。优势在于可以更改FXML加载组件的方式。

FXMLLoader通过嵌套控制器的经典方式来实例化组件:先是FXML,然后是每个部分的控制器。

而使用此技术是:先是控制器,然后是每个组件的FXML。并且您不会直接在FXML中加载FXML,而是在FXML中导入自定义的Java类。

这是更好的抽象(在导入组件时无需知道组件如何实现),有助于重用代码,就像实现具有FXML支持的自定义小部件一样。为了使组件可重用,请确保其实现与其他部分没有紧密耦合,或者使用IOC进行处理(例如,使用Spring集成JavaFX)。这样,您就可以在应用程序的任何部分导入组件(就像DateInput小部件一样)而无需担心,也不会重复代码。

在您的情况下,您将拥有:

public class MenuBox extends VBox {

@FXML
private LoginBox loginBox;

@FXML
private ProfilesBox profilesBox;

public MenuBox() {
    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("menu.fxml"));
    fxmlLoader.setRoot(this);
    fxmlLoader.setController(this);
    try {
        fxmlLoader.load();
    } catch (IOException exception) {
        throw new RuntimeException(exception);
    }
}

public class LoginBox extends VBox {
public LoginBox() {
    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("login.fxml"));
    fxmlLoader.setRoot(this);
    fxmlLoader.setController(this);
    try {
        fxmlLoader.load();
    } catch (IOException exception) {
        throw new RuntimeException(exception);
    }
}

public class ProfilesBox extends VBox {
public ProfilesBox() {
    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("profiles.fxml"));
    fxmlLoader.setRoot(this);
    fxmlLoader.setController(this);
    try {
        fxmlLoader.load();
    } catch (IOException exception) {
        throw new RuntimeException(exception);
    }
}

你需要在管理页面全局布局的menu.fxml中导入LoginBox和ProfilesBox:

<?import com.foo.bar.LoginBox ?>
<?import com.foo.bar.ProfilesBox ?>
<fx:root type="javafx.scene.layout.VBox"
    xmlns:fx="http://javafx.com/fxml">

<!-- Stuff here to declare the menu bar-->

    <HBox>
       <ProfilesBox fx:id="profilesBox"/>
       <LoginBox fx:id="loginBox"/>
    </HBox>

</fx:root>

login.fxml和profiles.fxml只包含基本组件。


我有点理解你的意思,但我觉得我没有完全掌握。你有更详细的例子吗? - j will
创建自己的小部件(例如ProfilesBox和LoginBox)并将它们放置在FXML文件中是可以的吗?login.fxml和profiles.fxml长什么样子? - j will
1
为了添加到已有的示例中,您只需要确保将自定义控件导入到 FXML 中。例如:<?import com.foo.bar.LoginBox ?>。 - chooks
1
除了这个答案,这篇文章也有帮助:http://www.javacodegeeks.com/2013/03/javafx-2-with-spring.html - j will

5
  1. 您可以将一个FXML文档嵌入到另一个文档中 - 这应该有助于您分离设计逻辑。

  2. 这意味着您可以拥有嵌套控制器 - 每个文档都可以有一个。

从文档中,您现在可以设置您的代码,使得逻辑可以分离,并且可以从根控制器调用(如果需要)。

希望这能帮到您。


2
我已经查看了“嵌套控制器”文档,如果我能够看到包含FXML的内容、主控制器初始化方法中有什么以及对话框控制器的外观,我认为那会非常有帮助。我只是不明白详细信息是如何起作用的。 - j will

1

我需要一个弹出窗口,具有类似的要求(对节点和布局有更多的控制)。

在经过推荐后,我找到了一个可能有用的解决方案。

首先,我创建了第二个fxml文档和第二个控制器(在NetBeans中,新建->空的FXML...->使用Java控制器->创建新...)。

有点挑战的是如何在主控制器中构建舞台并将其连接到弹出控制器。

链接传递参数JavaFX FXML提供了一些真正好的见解和技巧。

最终代码看起来像这样(希望能帮助某些人):

// Anchor Pane from the popup
@FXML
AnchorPane anchorPanePopup;

@FXML
private void soneButtonAction(ActionEvent event) throws IOException {     
    Stage newStage = new Stage();
    AnchorPane anchorPanePopup = (AnchorPane) FXMLLoader.load(getClass().getResource("Popup_FXML.fxml"));
    Scene scene = new Scene(anchorPanePopup);
    newStage.setScene(scene);
    newStage.initModality(Modality.APPLICATION_MODAL);
    newStage.setTitle("Dialog Window");
    newStage.showAndWait();        
}

0
package javafxapplication11;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.stage.Stage;

public class FXMLDocumentController implements Initializable {
@FXML
private CheckBox c1;

@FXML
private CheckBox c2;

public void clicked1(ActionEvent e) throws IOException {
Parent home_page_parent 
=FXMLLoader.load(getClass().getResource("AddDcuFXML.fxml"));
 Scene home_page_scene = new Scene(home_page_parent);
 Stage app_stage = (Stage) ((Node) e.getSource()).getScene().getWindow();
  app_stage.hide(); //optional
  app_stage.setScene(home_page_scene);
  app_stage.show();
   }
   public void clicked2(ActionEvent e) throws IOException {
   Parent home_page_parent 
    =FXMLLoader.load(getClass().getResource("ViewDCU.fxml"));
   Scene home_page_scene = new Scene(home_page_parent);
   Stage app_stage = (Stage) ((Node) e.getSource()).getScene().getWindow();
    app_stage.hide(); //optional
      app_stage.setScene(home_page_scene);
         app_stage.show();
     }
      public void clicked3(ActionEvent e) throws IOException {
     Parent home_page_parent 
   =FXMLLoader.load(getClass().getResource("ViewDCU.fxml"));
    Scene home_page_scene = new Scene(home_page_parent);
    Stage app_stage = (Stage) ((Node) e.getSource()).getScene().getWindow();
     app_stage.hide(); //optional
     app_stage.setScene(home_page_scene);
     app_stage.show();
      }
     @Override
       public void initialize(URL arg0, ResourceBundle arg1) {
        // TODO Auto-generated method stub
           } 
               }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接