web-dev-qa-db-de.com

Wechseln Sie in JavaFX zwischen den Fenstern

Ich versuche, ein Java-Programm in JavaFX mit FXML zu erstellen. Ich habe jedoch Probleme mit der Layoutverwaltung. Ich möchte zwischen den Fenstern wechseln, so wie ich es von CardLayout gewohnt bin, aber ich scheine es nicht zu verstehen.

Ich googelte herum und fand keine Antworten.

Gibt es in JavaFX ein gleichwertiges CardLayout? Und wenn ja, können Sie mir ein Beispiel geben? Das würde meinem Abend sehr helfen!

Hier ist mein FXML-Code

    <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>
20
chribsen

Nicht animierte Übergänge

Wenn Sie keine animierten Übergänge zwischen Ihren Fenstern benötigen, können Sie Folgendes tun:

  1. Ersetzen Sie die gesamte Szene, indem Sie eine neue Szene erstellen und diese Szene auf Ihrer Bühne einstellen ODER
  2. Ersetzen Sie nur einen bestimmten Bereich in einem übergeordneten Layout, indem Sie den alten Bereich aus dem übergeordneten Layout entfernen und Ihren neuen Bereich hinzufügen (indem Sie die children-Liste ) des Elternteils ODER bearbeiten
  3. Platzieren Sie alle Bereiche in einer StackPane und verschieben Sie den anzuzeigenden Bereich an den Anfang der untergeordneten Liste von stack .

Animierte Übergänge

Wenn Sie animierte Übergänge zwischen Ihren Fenstern wünschen, sehen Sie sich die zwei Teilserien von Angela Caicedo zum Verwalten mehrerer Bildschirme in JavaFX an:

Angelas Lösung ist die Verwendung einer StackPane mit einer separaten benutzerdefinierten ScreenController-Klasse zum Verwalten von Transitions oder Animationen zwischen den einzelnen Bereichen des Stapels.


Frameworks

Frameworks wie JFXFlow und WebFX können auch eine Browser-Stilschnittstelle für Ihre App bereitstellen, mit der Benutzer mithilfe der Zurück- und Vorwärts-Tasten und einer Verlaufsliste zwischen Bildschirmen wechseln können.

Update 2017

Ich denke, dass die Entwicklung in beiden oben genannten Frameworks jetzt nicht mehr funktioniert. Andere Frameworks, die sich in der Entwicklung befinden, sind:

Und zahlreiche andere (ich werde hier keine umfassende Liste erstellen).


Verbunden

31
jewelsea

So mache ich es: (In diesem Beispiel habe ich zwei FXML-Dokumente mit ihren entsprechenden Controllern erstellt. Sie heißen FXMLLogin.fxml bzw. Home.fxml).

Um von FXMLLogin zu Home zu gelangen,

In diesem Beispiel habe ich eine Methode im FXMLLoginController erstellt, die auf die Schaltfläche "login" in diesem Formular antwortet:

@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");
    }
}

Beachten Sie, dass @FXML äußerst wichtig ist.

Wenn ich Ihre Frage richtig verstanden habe, sollte dies der Trick sein.

Das Wechseln zwischen den Fenstern ist überhaupt nicht offensichtlich und wird in keinem der Tutorials im Web, die ich gefunden habe, klar umrissen. Ich musste mich ausgiebig googeln, bevor ich es zum ersten Mal herausfand. Zum Glück ist es eigentlich ganz einfach, wenn man den Dreh raus hat.

Ich hoffe, ich habe Ihre Frage nicht falsch verstanden. Lass es mich wissen, wenn du das brauchst :)

4
CodingAddict

JRebirth Application Framework bietet ein benutzerdefiniertes 'CardLayout' unter Verwendung seines speziellen Musters wB-CSMvc.

Die Klasse StackModel erledigt die Aufgabe (bereitgestellt von org.jrebirth.af:component-Artefakt). Sie finden 2 Verwendungen hier und hier .

Jedes "Karten" -Modell kann mit einer Modellkennung aufgerufen werden, und jeder Stapel hat einen eindeutigen Namen.

Das erste Beispiel wird für die JRebirth Demo-Anwendung verwendet. Es ist eine recht einfache Anwendung, die es ermöglicht, andere JRebirth-Vitrinenanwendungen anzuzeigen, die dynamisch als JRebirth-Modul geladen werden (aus einem separaten und unabhängigen Glas).

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();
    }
}

Diese Anwendung lädt ihr erstes Modell (MainModel) und legt ihren Stammknoten in den Szenen-Wurzelknoten (StakPane, automatisch erstellt).

Das MainModel listet alle Untermodule der Anwendung auf, um Schaltflächeneinträge im linken Menü und ein StackModel hinzuzufügen, das jeden Modulinhalt anzeigt. Das StackModel wird mit einer speziellen Anmerkung unter Verwendung seines eindeutigen String-Schlüssels geladen.

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;
    }
}

Das MainView ist für die Erstellung des Modulmenüs verantwortlich:

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;
    }
}

Der MainController lädt den Modulinhalt, wenn eine Menüschaltfläche ausgelöst wird:

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"));
    }
}

Im zweiten Beispiel wird das StackModel als innerComponent geladen, und jede Karte wird durch einen Aufzählungseintrag (in FXMLPage gespeichert) identifiziert. Schauen wir uns FXMLShowCaseModel an:

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

Die Aufzählung, die den Aufzählungseintrag mit Model verknüpft:

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;
    }
}

Da die Kartenliste bekannt ist, werden Symbolleistenelemente statisch in FXMLShowCaseView erstellt, und die Ereignisbehandlung wird auch in FXMLShowCaseController mithilfe einer anderen Technik statisch definiert:

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));

    }
}

Lassen Sie mich wissen, wenn Sie Fragen haben

0
Sébastien B.