JavaFX 8 - Qt 的 StackedWidget 的布局交换或 similar/equivalent 功能?

JavaFX 8 - Layout swapping or similar/equivalent functionality to Qt's StackedWidget?

有人要求我在 JavaFX 中编写一个转换程序,但我需要允许用户根据转换方向设置不同的选项。

为了交换转换方向,我需要为仅与当前方向相关的选项显示两组不同(唯一)的控件。

我需要在一个方向上显示两个 TextField,在另一个方向上显示一对 RadioButton。我可以同时显示两者,并在需要时只显示 enable/disable,但我首先尝试一种不那么混乱的方法。

我正在寻找一种解决方案,它具有与我在 C++ 中使用的 Qt 的 StackedWidget 类似的布局切换功能,因此我可以根据转换方向将 TextFields 换成 RadioButtons,反之亦然。

需要注意的是,这个window还有很多其他选项是双向通用的,所以根据转换方向需要改变的只是一小部分。因此,如果我可以轻松地从同一控制器中访问交换的控件,我会更喜欢它。

我不想要制表符或页码,因为用户可以在别处控制方向,所以 TabPane 和 Pagination 已经过时了,除非可以禁用那些不需要的功能。

我听说在另一个 Java 框架中有一个叫做 CardLayout 的东西(如果我没听错的话,它在 awt 中)可以完成我想要的工作,什么是 JavaFX 8 等价物?或者我应该使用另一种解决方案吗?

我正在使用 SceneBuilder,所以理想情况下我可以在其中实现,但如果需要我可以使用纯代码。

您可以使用任何 Pane 子类(例如 StackPane)并根据需要调用 pane.getChildren().setAll(textFieldDisplay);pane.getChildren().setAll(radioButtonDisplay);。不同的显示可以是任何类型的 Node,但由于它们包含其他控件,因此它们通常也是 Pane 的某个子类。在下面的示例中,我对其中一个使用 GridPane,对另一个使用 VBox。在实际应用程序中,您可能会在其自己的 FXML 文件中定义每一个并独立加载它们,等等。

完整示例(使用 FXML):

Main.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>  

<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController"
    alignment="CENTER">

    <padding>
        <Insets top="20" left="20" right="20" bottom="20" />
    </padding>

    <CheckBox text="Show Text Fields" fx:id="showTextFields" VBox.vgrow="NEVER">
        <VBox.margin>
            <Insets top="10" left="10" right="10" bottom="10"/>
        </VBox.margin>
    </CheckBox>
    <StackPane fx:id="display" VBox.vgrow="ALWAYS" />

    <Button  text="OK" onAction="#submit" VBox.vgrow="NEVER" />
</VBox>

MainController.java:

package application;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.StackPane;

public class MainController {
    @FXML
    private CheckBox showTextFields ;
    @FXML
    private StackPane display ;

    private Node radioDisplay ;
    private Node textFieldDisplay ;

    private RadioButtonController radioButtonController ;
    private TextFieldController textFieldController ;

    public void initialize() throws Exception {

        FXMLLoader radioDisplayLoader = new FXMLLoader(getClass().getResource("RadioDisplay.fxml"));
        radioDisplay = radioDisplayLoader.load();
        radioButtonController = radioDisplayLoader.getController();

        FXMLLoader textFieldDisplayLoader = new FXMLLoader(getClass().getResource("TextFieldDisplay.fxml"));
        textFieldDisplay = textFieldDisplayLoader.load();
        textFieldController = textFieldDisplayLoader.getController();

        showTextFields.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
            if (isSelected) {
                display.getChildren().setAll(textFieldDisplay);
            } else {
                display.getChildren().setAll(radioDisplay);
            }
        });

        display.getChildren().add(radioDisplay);
    }

    @FXML
    private void submit() {
        if (showTextFields.isSelected()) {
            System.out.println("Value 1 is "+ textFieldController.getText1());
            System.out.println("Value 2 is "+ textFieldController.getText2());
        } else {
            System.out.println("Chosen value is "+radioButtonController.getSelectedItem());
        }
    }
}

RadioDisplay.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.RadioButton?>

<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.RadioButtonController"
    alignment="TOP_CENTER" spacing="10">

    <padding>
        <Insets top="10" left="10" right="10" bottom="10"/>
    </padding>

    <RadioButton text="Choice 1" selected="true" fx:id="choice1"/>
    <RadioButton text="Choice 2" fx:id="choice2"/>
</VBox>

RadioButtonController.java:

package application;

import javafx.fxml.FXML;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;

public class RadioButtonController {
    @FXML
    private RadioButton choice1 ;
    @FXML
    private RadioButton choice2 ;

    public void initialize() {

        ToggleGroup toggleGroup = new ToggleGroup();
        choice1.setToggleGroup(toggleGroup);
        choice2.setToggleGroup(toggleGroup);
    }

    public String getSelectedItem() {
        if (choice1.isSelected()) {
            return "Choice 1";
        } else if (choice2.isSelected()) {
            return "Choice 2";
        } else return "" ;
    }
}

TextFieldDisplay.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>

<GridPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.TextFieldController"
    hgap="10" vgap="10">

    <columnConstraints>
        <ColumnConstraints hgrow="NEVER" halignment="RIGHT"/>
        <ColumnConstraints hgrow="SOMETIMES" />
    </columnConstraints>

    <Label text="Value 1:" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
    <Label text="Value 2:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>

    <TextField fx:id="textField1" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
    <TextField fx:id="textField2" GridPane.columnIndex="1" GridPane.rowIndex="1"/>

</GridPane>

TextFieldController.java:

package application;

import javafx.fxml.FXML;
import javafx.scene.control.TextField;

public class TextFieldController {
    @FXML
    private TextField textField1 ;
    @FXML
    private TextField textField2 ;

    public String getText1() {
        return textField1.getText() ;
    }
    public String getText2() {
        return textField2.getText();
    }
}

Main.java:

package application;

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.fxml.FXMLLoader;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox root = (VBox)FXMLLoader.load(getClass().getResource("Main.fxml"));
        Scene scene = new Scene(root,400,400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

所有 FXML 文件都在与 .java 文件相同的包 (application) 中。

更新

如果您不希望 "modularize" 达到这种程度,您可以将所有内容放在一个具有单个控制器的 FXML 中。在那种情况下,Main.fxml 看起来像

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>  
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>

<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController"
    alignment="CENTER">

    <padding>
        <Insets top="20" left="20" right="20" bottom="20" />
    </padding>

    <CheckBox text="Show Text Fields" fx:id="showTextFields" VBox.vgrow="NEVER">
        <VBox.margin>
            <Insets top="10" left="10" right="10" bottom="10"/>
        </VBox.margin>
    </CheckBox>
    <StackPane fx:id="display" VBox.vgrow="ALWAYS">
        <VBox fx:id="radioDisplay" alignment="TOP_CENTER" spacing="10">

            <padding>
                <Insets top="10" left="10" right="10" bottom="10" />
            </padding>

            <RadioButton text="Choice 1" selected="true" fx:id="choice1" />
            <RadioButton text="Choice 2" fx:id="choice2" />
        </VBox>

        <fx:define>
            <GridPane fx:id="textFieldDisplay" hgap="10" vgap="10">

                <columnConstraints>
                    <ColumnConstraints hgrow="NEVER" halignment="RIGHT" />
                    <ColumnConstraints hgrow="SOMETIMES" />
                </columnConstraints>

                <Label text="Value 1:" GridPane.columnIndex="0"
                    GridPane.rowIndex="0" />
                <Label text="Value 2:" GridPane.columnIndex="0"
                    GridPane.rowIndex="1" />

                <TextField fx:id="textField1" GridPane.columnIndex="1"
                    GridPane.rowIndex="0" />
                <TextField fx:id="textField2" GridPane.columnIndex="1"
                    GridPane.rowIndex="1" />

            </GridPane>
        </fx:define>
    </StackPane>

    <Button  text="OK" onAction="#submit" VBox.vgrow="NEVER" />
</VBox>

对应的控制器是

package application;

import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.StackPane;

public class MainController {
    @FXML
    private CheckBox showTextFields ;
    @FXML
    private StackPane display ;

    @FXML
    private Node radioDisplay ;
    @FXML
    private Node textFieldDisplay ;

    @FXML
    private TextField textField1 ;
    @FXML
    private TextField textField2 ;
    @FXML
    private RadioButton choice1 ;
    @FXML
    private RadioButton choice2 ;

    public void initialize() throws Exception {

        showTextFields.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
            if (isSelected) {
                display.getChildren().setAll(textFieldDisplay);
            } else {
                display.getChildren().setAll(radioDisplay);
            }
        });

        ToggleGroup toggleGroup = new ToggleGroup();
        choice1.setToggleGroup(toggleGroup);
        choice2.setToggleGroup(toggleGroup);
    }

    @FXML
    private void submit() {
        if (showTextFields.isSelected()) {
            System.out.println("Value 1 is "+ textField1.getText());
            System.out.println("Value 2 is "+ textField2.getText());
        } else {
            String chosenValue ;
            if (choice1.isSelected()) {
                chosenValue = "Choice 1";
            } else if (choice2.isSelected()) {
                chosenValue = "Choice 2";
            } else {
                chosenValue

 = "None";
            }

            System.out.println("Chosen value is "+chosenValue);
        }
    }
}

(在这种情况下,删除其他两个 FXML 文件及其控制器)。