web-dev-qa-db-fra.com

Basculer entre les volets dans JavaFX

J'essaie de créer un programme Java en JavaFX en utilisant FXML. Cependant, j'ai des problèmes avec la gestion de la mise en page. Je veux basculer entre les volets, comme je suis habitué avec CardLayout en swing, mais je n'arrive pas à comprendre.

J'ai cherché sur Google et je n'ai trouvé aucune réponse.

Existe-t-il un équivalent CardLayout dans JavaFX? et si oui, pouvez-vous me donner un exemple? Cela aiderait beaucoup ma soirée!

Voici mon code 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>
20
chribsen

Transitions non animées

Si vous n'avez pas besoin de transitions animées entre vos volets, vous pouvez:

  1. Remplacez la scène entière en créant une nouvelle scène et définissez cette scène sur votre scène OU
  2. Remplacez simplement un volet spécifique d'une disposition parent en supprimant l'ancien volet de son parent et en ajoutant votre nouveau volet (en manipulant la liste des enfants du parent ) OU
  3. Placez tous vos volets dans une StackPane et déplacez le volet que vous souhaitez afficher en haut de la liste des enfants de la pile .

Transitions animées

Si vous souhaitez des transtions animées entre vos volets, consultez la série en deux parties d’Angela Caicedo sur la gestion de plusieurs écrans dans JavaFX:

La solution d'Angela consiste à utiliser un StackPane avec une classe ScreenController personnalisée distincte pour la gestion de Transitions ou des animations entre les volets de la pile.


Cadres

Des cadres tels que JFXFlow et WebFX peuvent également fournir une interface de style navigateur pour votre application, permettant aux utilisateurs de basculer d'un écran à l'autre à l'aide de boutons Précédent/Suivant et d'une liste d'historique.

Mise à jour 2017

Je pense que le développement sur les deux référentiels référencés ci-dessus est maintenant obsolète. Les autres cadres en cours de développement sont:

Et de nombreux autres (je ne fournirai pas une liste complète ici).


En relation

31
jewelsea

Voici comment je le fais: .__ (Dans cet exemple, j'ai créé deux documents FXML avec leurs contrôleurs correspondants. Ils s'appellent respectivement FXMLLogin.fxml et Home.fxml).

Donc, pour aller de FXMLLogin à la maison,

Dans cet exemple, j'ai créé une méthode dans le FXMLLoginController qui répond au bouton "connexion" de ce formulaire en cours d'utilisation:

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

Notez que le @FXML est extrêmement important.

Si j'ai bien compris votre question, cela devrait faire l'affaire.

La commutation entre les volets n’est pas évidente et ne figure pas clairement dans aucun des tutoriels en ligne que j’ai trouvés. Je devais google longuement moi-même avant de comprendre pour la première fois. Heureusement, c'est en fait assez simple une fois que vous maîtrisez la situation.

J'espère que je n'ai pas mal compris votre question? Faites-moi savoir si c'est ce dont vous avez besoin :)

4
CodingAddict

JRebirth Application Framework fournit un «CardLayout» personnalisé à l'aide de son modèle dédié wB-CSMvc.

La classe StackModel fera le travail (fournie par un artefact de composant org.jrebirth.af:component), vous pouvez trouver 2 utilisations ici et ici .

Chaque modèle de "carte" peut être appelé à l'aide d'un identifiant enum | modelKey, et chaque pile porte un nom unique.

Le premier exemple est utilisé pour Application de démonstration JRebirth , il s’agit d’une application assez simple qui permet d’afficher d’autres applications de démonstration JRebirth chargées dynamiquement en tant que module JRebirth (à partir d’un fichier jar séparé et indépendant).

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

Cette application chargera son premier modèle (MainModel) et placera son nœud racine dans le nœud racine de la scène (StakPane, construit automatiquement).

MainModel listera tous les sous-modules de l'application pour ajouter une entrée de bouton dans son menu de gauche et un StackModel qui affichera le contenu de chaque module . StackModel est chargé à l'aide d'une annotation spéciale à l'aide de sa clé unique 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 se chargera de créer le menu du module:

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

Et MainController chargera le contenu du module quand un bouton de menu sera déclenché:

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

Le deuxième exemple chargera StackModel en tant que innerComponent et chaque carte sera identifiée par une entrée enum (stockée dans FXMLPage), voyons FXMLShowCaseModel:

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

L'énumération qui relie l'entrée enum à Model:

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

Comme la liste des cartes est connue, les éléments de la barre d’outils sont créés de manière statique dans FXMLShowCaseView et la gestion des événements est également définie de manière statique dans FXMLShowCaseController à l’aide d’une autre technique:

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

    }
}

Faites-moi savoir si vous avez des questions

0
Sébastien B.