27

我正在尝试使用 FXML 在 JavaFX 中制作 Java 程序。但是我在布局管理方面遇到了麻烦。我想在窗格之间切换,就像我习惯于在摇摆中使用 CardLayout 一样,但我似乎无法理解。

我用谷歌搜索并没有找到任何答案。

JavaFX中是否有任何等效的CardLayout?如果是这样,你能给我举个例子吗?这对我的晚上有很大帮助!

这是我的 FXML 代码

    <AnchorPane id="anchorPane" prefHeight="324.0" prefWidth="530.0" xmlns:fx="http://javafx.com/fxml" fx:controller="javafxapplication2.SampleController">
  <children>
    <Pane fx:id="mainScreen" layoutX="6.0" prefHeight="324.0" prefWidth="518.0">
      <children>
        <Button layoutX="254.0" layoutY="37.0" mnemonicParsing="false" text="Button" />
      </children>
    </Pane>
    <Pane fx:id="loginScreen" prefHeight="324.0" prefWidth="530.0">
      <children>
        <TextField id="password" fx:id="username" layoutX="142.0" layoutY="106.0" prefWidth="200.0" />
        <TextField fx:id="password" layoutX="142.0" layoutY="140.0" prefWidth="200.0" />
        <Label fx:id="label" layoutX="126.0" layoutY="120.0" minHeight="16.0" minWidth="69.0" />
        <Button fx:id="button" layoutX="213.0" layoutY="196.0" onAction="#handleButtonAction" onKeyPressed="#handleButtonAction" text="Login" />
      </children>
    </Pane>
  </children>
</AnchorPane>
4

3 回答 3

42

非动画过渡

如果您不需要窗格之间的动画过渡,那么您可以:

  1. 通过创建新场景替换整个场景并将该场景设置在舞台上
  2. 仅替换父布局中的特定窗格,方法是从其父窗格中删除旧窗格并添加新窗格(通过操作父窗格的子列表)或
  3. 将所有窗格放在 a 中StackPane,然后将要显示的窗格移动到堆栈子列表的顶部。

动画过渡

如果您想在窗格之间进行动画转换,请参阅 Angela Caicedo 关于在 JavaFX 中管理多个屏幕的两部分系列:

Angela 的解决方案是使用具有单独自定义 ScreenController 类的StackPane来管理堆栈中窗格之间的转换动画。


构架

JFXFlowWebFX等框架还可以为您的应用程序提供浏览器样式的界面,允许用户使用后退和前进按钮以及历史列表在屏幕之间来回切换。

2017 年更新

我认为上面提到的两个框架的开发现在都已经不复存在了。其他正在开发的框架有:

还有很多其他的(我不会在这里提供一个完整的列表)。


有关的

于 2013-04-23T21:07:23.403 回答
5

我是这样做的:(在本例中,我创建了两个 FXML 文档及其相应的控制器。它们分别称为 FXMLLogin.fxml 和 Home.fxml)。

所以,从 FXMLLogin 到 Home,

在此示例中,我在 FXMLLoginController 中创建了一个方法,该方法响应被按下的表单上的“登录”按钮:

@FXML
private void login(javafx.event.ActionEvent event) throws IOException
{
    if(pwf1.getText().equals("alphabetathetagamma"))
    {
            Parent blah = FXMLLoader.load(getClass().getResource("Home.fxml"));
            Scene scene = new Scene(blah);
            Stage appStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
            appStage.setScene(scene);
            appStage.show();
    }
    else
    {
            label1.setText("Password is incorrect. Please Try Again");
    }
}

请注意,@FXML 非常重要。

如果我正确理解了您的问题,那么这应该可以解决问题。

在窗格之间切换一点也不明显,并且在我发现的网络上的任何教程中都没有清楚地概述。在我第一次弄清楚之前,我不得不自己广泛地用谷歌搜索。幸运的是,一旦你掌握了它,它实际上非常简单。

希望我没有误解你的问题?让我知道这是否是您需要的:)

于 2015-11-16T07:46:27.647 回答
0

JRebirth 应用程序框架使用其专用模式 wB-CSMvc 提供自定义“CardLayout”。

StackModel类将完成这项工作(由 org.jrebirth.af:component artifact 提供),您可以在此处此处找到 2 个用法。

每个“卡片”模型都可以使用 enum|modelKey 标识符来调用,并且每个堆栈都有一个唯一的名称。

第一个示例用于JRebirth 演示应用程序,它是一个非常简单的应用程序,可以显示其他 JRebirth 展示应用程序作为 JRebirth 模块动态加载(来自单独的独立 jar)。

public final class JRebirthDemo extends DefaultApplication<StackPane> {

    public static void main(final String... args) {
        Application.launch(JRebirthDemo.class, args);
    }

    @Override
    public Class<? extends Model> firstModelClass() {
        return MainModel.class;
    }

    @Override
    protected String applicationTitle() {
        return "JRebirth Demo Application";
    }

    @Override
    protected void customizeScene(final Scene scene) {
        super.customizeScene(scene);

        addCSS(scene, DemoStyles.DEFAULT);
        addCSS(scene, WorkbenchStyles.DEFAULT);
    }

    @Override
    protected void customizeStage(final Stage stage) {
        // Center the stage
        stage.centerOnScreen();
    }

    @Override
    protected List<? extends ResourceItem<?, ?, ?>> getResourceToPreload() {
        return Collections.emptyList();
    }
}

此应用程序将加载其第一个模型(MainModel)并将其根节点放入场景根节点(StakPane,自动构建)。

MainModel 将列出所有应用程序的子模块以在其左侧菜单中添加一个按钮条目,以及一个将显示每个模块内容的 StackModel。StackModel 是使用其唯一的 String 键使用特殊注释加载的。

public final class MainModel extends DefaultModel<MainModel, MainView> {

    private final List<ModuleModel> modules = new ArrayList<>();

    @Link("DemoStack")
    private StackModel stackModel;

    @Override
    protected void initModel() {
        for (final ModuleModel mm : getModels(ModuleModel.class)) {
            this.modules.add(mm);
        }
    }

    @Override
    protected void showView() {
        view().node().setCenter(this.stackModel.node());
    }

    @Override
    protected void hideView() {
        // Nothing to do yet

    }

    List<ModuleModel> getModules() {
        return this.modules;
    }
}

MainView 将负责创建模块菜单:

public final class MainView extends DefaultView<MainModel, BorderPane, MainController> {

    private final List<Button> buttonList = new ArrayList<>();

    public MainView(final MainModel model) throws CoreException {
        super(model);
    }

    @Override
    protected void initView() {

        node().setPrefSize(800, 600);

        node().setLeft(createMenu());

    }

    @Override
    public void start() {
        this.buttonList.stream().findFirst().ifPresent(button -> button.fire());
    }

    private Node createMenu() {
        final VBox box = new VBox();

        for (final ModuleModel mm : model().getModules()) {
            final Node n = createModuleButton(mm);
            VBox.setMargin(n, new Insets(4, 4, 4, 4));
            box.getChildren().add(n);
        }
        return box;
    }

    private Node createModuleButton(final ModuleModel mm) {
        final Button b = new Button(mm.moduleName());
        b.getStyleClass().add("menuButton");
        b.setPrefSize(100, 50);
        b.setOnAction(controller()::onButtonFired);
        b.setUserData(Key.create(mm.getClass()));
        this.buttonList.add(b);
        return b;
    }
}

当触发任何菜单按钮时,MainController 将加载模块内容:

public final class MainController extends DefaultController<MainModel, MainView> implements ActionAdapter {

    public MainController(final MainView view) throws CoreException {
        super(view);
    }

    public void onButtonFired(final ActionEvent event) {
        final Button b = (Button) event.getSource();
        final UniqueKey<? extends Model> data = (UniqueKey<? extends Model>) b.getUserData();

        model().sendWave(StackWaves.SHOW_PAGE_MODEL,
                         WBuilder.waveData(StackWaves.PAGE_MODEL_KEY, data),
                         WBuilder.waveData(StackWaves.STACK_NAME, "DemoStack"));
    }
}

第二个示例将 StackModel 作为 innerComponent 加载,每张卡片将由一个枚举条目(存储在 FXMLPage 中)标识,让我们看看 FXMLShowCaseModel :

final InnerComponent<StackModel> stack = CBuilder.innerComponent(StackModel.class, FXMLPage.class);
    this.stackModel = findInnerComponent(stack);

将枚举条目与模型链接的枚举:

public enum FXMLPage implements PageEnum {

    StandaloneFxml,
    IncludedFxml,
    ViewEmbeddedFxml,
    HybridFxml;

    @Override
    public UniqueKey<? extends Model> getModelKey() {
        UniqueKey<? extends Model> modelKey;

        switch (this) {

            default:
            case ViewEmbeddedFxml:
                modelKey = Key.create(EmbeddedModel.class);
                break;
            case StandaloneFxml:
                modelKey = Key.create(StandaloneModel.class);
                break;
            case HybridFxml:
                modelKey = Key.create(HybridModel.class, FXMLModel.KEYPART_FXML_PREFIX + "org.jrebirth.af.showcase.fxml.ui.hybrid.Hybrid");
                break;
            case IncludedFxml:
                modelKey = Key.create(IncludedModel.class, new LoremIpsum());
                break;
        }

        return modelKey;
    }
}

由于卡片列表是已知的,工具栏项静态创建到 FXMLShowCaseView 中,事件处理也使用另一种技术静态定义到 FXMLShowCaseController 中:

public final class FXMLShowCaseController extends DefaultController<FXMLShowCaseModel, FXMLShowCaseView> {

    private static final Logger LOGGER = LoggerFactory.getLogger(FXMLShowCaseController.class);

    public FXMLShowCaseController(final FXMLShowCaseView view) throws CoreException {
        super(view);
    }

    @Override
    protected void initEventAdapters() throws CoreException {

        // WaveData<Class<? extends PageEnum>> stackName = Builders.waveData(StackWaves.STACK_PAGES, FXMLShowCaseModel.STACK_PAGES);

        // Manage Ui Command Button
        linkWave(view().getShowIncluded(), ActionEvent.ACTION, StackWaves.SHOW_PAGE_ENUM,
                 WBuilder.waveData(StackWaves.PAGE_ENUM, FXMLPage.IncludedFxml));

        linkWave(view().getShowEmbedded(), ActionEvent.ACTION, StackWaves.SHOW_PAGE_ENUM,
                 WBuilder.waveData(StackWaves.PAGE_ENUM, FXMLPage.ViewEmbeddedFxml));

        linkWave(view().getShowStandalone(), ActionEvent.ACTION, StackWaves.SHOW_PAGE_ENUM,
                 WBuilder.waveData(StackWaves.PAGE_ENUM, FXMLPage.StandaloneFxml));

        linkWave(view().getShowHybrid(), ActionEvent.ACTION, StackWaves.SHOW_PAGE_ENUM,
                 WBuilder.waveData(StackWaves.PAGE_ENUM, FXMLPage.HybridFxml));

    }
}

如果您有任何问题,请告诉我

于 2017-01-11T08:43:50.467 回答