6

我对 java 和 javafx 很陌生,遇到了一个我无法解决的问题。我需要将新的自定义控件动态添加到 javafx 场景。此外,我需要主控件和添加的控件之间的交互。我已经在网上找到了一些有用的信息,但无法将它们放在一起。

所以我建立了一个小例子来解释:

主类:

public class Test_TwoController extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("Fxml1.fxml"));
        Scene scene = new Scene(root);                  
        stage.setScene(scene);
        stage.show();
    }    
    public static void main(String[] args) {
        launch(args);
    }
}

主要的fxml:

<AnchorPane id="fxml1_anchorpane_id" fx:id="fxml1_anchorpane" prefHeight="206.0" prefWidth="406.0" xmlns:fx="http://javafx.com/fxml" fx:controller="test_twocontroller.Fxml1Controller">
  <children>
    <HBox id="fxml1_hbox_id" fx:id="fxml1_hbox" prefHeight="200.0" prefWidth="400.0">
      <children>
        <Button id="fxml1_button_id" fx:id="fxml1_button" mnemonicParsing="false" onAction="#button_action" prefHeight="200.0" prefWidth="200.0" text="Button" />
      </children>
    </HBox>
  </children>
</AnchorPane>

及其控制器:

public class Fxml1Controller implements Initializable {

    @FXML HBox hbox;
    @FXML Button button;

    @Override
    public void initialize(URL url, ResourceBundle rb) { }

    public void button_action(ActionEvent event) throws IOException {
        // 1. add an instance of Fxml2 to hbox
        // 2. change to tab2 in new Fxml2
        // or
        // notify Fxml2Controller to change to tab2 in Fxml2
    }
}

现在动态添加控件:

它的fxml:

<AnchorPane id="fxml2_anchorpane_id" fx:id="fxml2_anchorpane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="200.0" xmlns:fx="http://javafx.com/fxml" fx:controller="test_twocontroller.Fxml2Controller">
  <children>
    <TabPane id="fxml2_tabpane_id" fx:id="fxml2_tabpane" prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE">
      <tabs>
        <Tab id="fxml2_tab1_id" fx:id="fxml2_tab1" text="tab1">
          <content>
            <AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
          </content>
        </Tab>
        <Tab id="fxml2_tab2_id" fx:id="fxml2_tab2" onSelectionChanged="#onSelectionChanged" text="tab2">
          <content>
            <AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
          </content>
        </Tab>
      </tabs>
    </TabPane>
  </children>
</AnchorPane>

和控制器:

public class Fxml2Controller {

    @FXML TabPane tabpane;
    @FXML Tab tab1;
    @FXML Tab tab2;

    public Fxml2Controller() throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("Fxml2.fxml"));
        Scene scene = new Scene(root);        
        Stage stage = new Stage();
        stage.setScene(scene);        
    }    

    public void onSelectionChanged(Event e) throws IOException {

        FXMLLoader loader = new FXMLLoader();
        // how can i get the current Fxml1 anchorpane instance?
        AnchorPane root = (AnchorPane) loader.load(getClass().getResource("Fxml1.fxml").openStream());

        Button b = (Button)root.lookup("#fxml1_button_id");        
        b.setText("New Button Text"); // dont change the buttons text!!!               
}
}

用法是:在fxml1的hbox中添加一个fxml2。然后在 fxml1 中单击按钮后,fxml2 的选项卡应该会更改。你可以看看那个图片http://s13.postimage.org/uyrmgylo7/two_controlls.png

所以我的问题是:

  • 如何将一个或多个 fxml2 控制器添加到 fxml1 的 hbox 中?
  • 如何从另一个控件访问一个控件或在控件之间进行通信?有关详细信息,请参阅 Fxml2Controller 中的 onSelectionChanged() 方法。

提前谢谢你,solarisx

4

1 回答 1

6

您似乎将很多不同的概念混合在一起。首先,Stage可以理解为屏幕上的一个窗口。它有一个包含实际 SceneGraph 的 Scene 对象。在您的示例中,您正在创建一个新阶段和一个新场景,它们将填充您的第二个 fxml 文件的内容。这意味着,如果工作,第二个窗口会弹出包含你的东西。我不认为这是你想要实现的。

此外,当 FXMLLoader 读取文件时,它会查找指定为其控制器的类并通过反射构造它的实例。这意味着当您在使用它加载的 fxml 文件的控制器的构造函数中调用 load 方法时,您将导致无限循环。

最后要理解的是,load()返回的对象是一个任意节点,可以像任何其他节点一样放入应用程序的 SceneGraph 中。

因此,要使您的概念发挥作用,您应该执行以下操作:

  1. 将当前位于第二个控制器的构造函数中的加载代码移动到第一个控制器的 button_Action 方法。
  2. 扔掉button_action中的new-stage-new-scene代码,取FXMLLoader返回的Node,添加到HBox的children中。
  3. 对于第二个问题,如果您实际创建 FXMLLoader 的实例而不是调用静态方法,则可以获取控制器实例,并使用其中的load()方法。调用后,您可以通过和load()检索 fxml 文件的控制器和根对象。然后,您可以像使用逻辑中的任意对象一样使用它们。getController()getRoot()
于 2012-11-20T09:24:30.667 回答