Switch between panes in JavaFX

后端 未结 3 859
醉话见心
醉话见心 2020-12-02 17:22

I\'m trying to make a Java program in JavaFX using FXML. However i\'m having trouble with the layout management. I want to switch between Panes, just as i\'m used to with Ca

相关标签:
3条回答
  • 2020-12-02 17:55

    Here's how I do it: (In this example, I have created two FXML documents with their corresponding controllers. They are called FXMLLogin.fxml, and Home.fxml, respectively).

    So, to go from FXMLLogin to Home,

    In this example, I created a method within the FXMLLoginController which responds to the "login" button on that form being pressed:

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

    Note that the @FXML is extremely important.

    If I understood your question correctly, then this should do the trick.

    Switching between panes is not at all obvious, and is not outlined clearly on any tutorial on the web that I have found. I had to google extensively myself before I figured it out for the first time. Luckily, it's actually quite simple once you get the hang of it.

    I hope I did not misunderstand your question? Let me know if this is what you need:)

    0 讨论(0)
  • 2020-12-02 17:58

    JRebirth Application Framework provides a custom 'CardLayout' using its dedicated pattern wB-CSMvc.

    The StackModel class will do the job (provided by org.jrebirth.af:component artifact), you can find 2 usages here and here.

    Each 'card' model can be called using a enum|modelKey identifier, and each stack has an unique name.

    The first sample is used for the JRebirth Demo Application, it's a pretty simple application that will allow to display other JRebirth showcase applications dynamycally loaded as JRebirth module (from a separate and independent 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();
        }
    }
    

    This application will load its first model (MainModel) and put its root node into the scene root node (StakPane, automatically built).

    The MainModel will list all application's sub modules to add a button entries into its left menu, and a StackModel that will display each module content. The StackModel is loaded using special annotation using its unique String key.

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

    The MainView will be in charge to create the module menu:

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

    And the MainController will load the Module content when any menu button is triggered:

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

    The second example will load the StackModel as an innerComponent and each card will be identified by an enum entry (stored into FXMLPage), let's see FXMLShowCaseModel :

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

    The enumeration that link enum entry with 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;
        }
    }
    

    As the card list is known, toolbar item are statically created into FXMLShowCaseView and event handling is also staically defined into FXMLShowCaseController using another 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));
    
        }
    }
    

    Let me know if you have any question

    0 讨论(0)
  • 2020-12-02 18:15

    Non-animated transitions

    If you don't need animated transitions between your panes, then you can:

    1. Replace the whole scene by creating a new scene and set that scene on your Stage OR
    2. Replace just a specific pane in a parent layout, by removing the old pane from it's parent and adding your new pane (by manipulating the parent's children list) OR
    3. Place all your Panes in a StackPane and move the pane you want to display to the top of the stack's child list.

    Animated transitions

    If you would like animated transtions between your panes, then see Angela Caicedo's two part series on managing multiple screens in JavaFX:

    • Part I
    • Part II

    Angela's solution is to use a StackPane with a separate custom ScreenController class for managing Transitions or animations between panes in the stack.


    Frameworks

    Frameworks like JFXFlow and WebFX can also provide a browser style interface for your app, allowing users to switch back and forth between screens using back and forward buttons and a history list.

    Update 2017

    I think development on both referenced frameworks above is now defunct. Other frameworks which are under development are:

    • TornadoFX
    • JRebirth
    • afterburner.fx

    And numerous others (I won't provide a comprehensive list here).


    Related

    • Loading new fxml in the same scene
    0 讨论(0)
提交回复
热议问题