Превключване между панелите в JavaFX

Опитвам се да направя Java програма в JavaFX с помощта на FXML. Въпреки това имам проблеми с управлението на оформлението. Искам да превключвам между панели, точно както съм свикнал с CardLayout в размах, но изглежда не мога да го разбера.

Търсих в Google и не намерих никакви отговори.

Има ли еквивалент на CardLayout в JavaFX? и ако е така, можете ли да ми дадете пример? Това ще ми помогне много вечерта!

Ето моят 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>

person chribsen    schedule 23.04.2013    source източник


Отговори (3)


Неанимирани преходи

Ако не се нуждаете от анимирани преходи между вашите панели, тогава можете:

  1. Заменете цялата сцена, като създадете нова сцена и задайте тази сцена на вашата сцена ИЛИ
  2. Заменете само конкретен панел в родителско оформление, като премахнете стария панел от неговия родител и добавите вашия нов панел (чрез манипулиране на родителския списък с деца) ИЛИ
  3. Поставете всичките си панели в StackPane и преместете панела, който искате да покажете, в горната част на списък с деца на стека.

Анимирани преходи

Ако искате анимирани преходи между вашите панели, вижте поредицата от две части на Анджела Кайседо за управление на множество екрани в JavaFX:

Решението на Анджела е да използва StackPane с отделен персонализиран клас ScreenController за управление на преходи или анимации между панелите в стека.


Рамки

Рамки като JFXFlow и WebFX може също така да предостави интерфейс в стил на браузър за вашето приложение, което позволява на потребителите да превключват напред и назад между екрани с помощта на бутони за връщане назад и напред и списък с хронология.

Актуализация 2017

Мисля, че разработката на двете референтни рамки по-горе вече не съществува. Други рамки, които са в процес на разработка, са:

И много други (няма да предоставя изчерпателен списък тук).


Свързани

person jewelsea    schedule 23.04.2013

Ето как го правя: (В този пример създадох два 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 е изключително важен.

Ако съм разбрал въпроса ви правилно, тогава това трябва да свърши работа.

Превключването между панелите изобщо не е очевидно и не е очертано ясно в нито един урок в мрежата, който намерих. Самият аз трябваше да ровя обстойно в Google, преди да го разбера за първи път. За щастие, всъщност е доста просто, след като хванете цаката.

Надявам се, че не съм разбрал погрешно въпроса ви? Уведомете ме дали това е, от което имате нужда :)

person CodingAddict    schedule 16.11.2015
comment
В моя случай преходът между двете е доста бавен (отнема около една секунда), не знам дали това е нормално? - person seinecle; 01.05.2020

JRebirth Application Framework предоставя персонализиран „CardLayout“, използвайки своя специален модел wB-CSMvc.

StackModel клас ще свърши работата (предоставен от org.jrebirth.af:component artifact), можете да намерите 2 употреби тук и тук.

Всеки модел на карта може да бъде извикан с помощта на идентификатор enum|modelKey и всеки стек има уникално име.

Първата проба се използва за приложението JRebirth Demo, това е доста просто приложение, което ще позволи на показване на други JRebirth демонстрационни приложения, динамично заредени като JRebirth модул (от отделен и независим буркан).

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 и всяка карта ще бъде идентифицирана чрез запис enum (съхранен в FXMLPage), нека видим FXMLShowCaseModel:

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

Изброяването, което свързва enum запис с модела:

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

    }
}

Уведомете ме, ако имате въпроси

person Sébastien B.    schedule 11.01.2017