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>
Transitions non animées
Si vous n'avez pas besoin de transitions animées entre vos volets, vous pouvez:
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
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 :)
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