Переключение между панелями в JavaFX
Я пытаюсь создать Java-программу в JavaFX, используя FXML. Однако у меня проблемы с управлением компоновкой. Я хочу переключаться между Panes, так же, как я привык к CardLayout в качели, но я не могу его получить.
Я googled вокруг и не нашел никаких ответов.
Есть ли эквивалент 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>
Ответы
Ответ 1
Неанимированные переходы
Если вам не нужны анимированные переходы между вашими панелями, вы можете:
- Замените всю сцену, создав новую сцену, и установите эту сцену на своей сцене ИЛИ
- Замените только конкретную панель в родительском макете, удалив из нее старую панель и добавив новую панель (манипулируя родительским дочерним списком) ИЛИ
- Поместите все ваши панорамы в
StackPane
и переместите панель, которую вы хотите отобразить, в верхней части дочерний список стека.
Анимированные переходы
Если вы хотите анимированные трансионы между вашими панелями, обратитесь к Анжеле Кайседо из двух частей серии по управлению несколькими экранами в JavaFX:
Решение Angela заключается в использовании StackPane с отдельным пользовательским классом ScreenController для управления Переходы или animations между панелями в стеке.
Каркасы
Рамки, такие как JFXFlow и WebFX также может обеспечить интерфейс браузера для вашего приложения, позволяя пользователям переключаться между экранами с помощью кнопок "Назад" и "Вперед" и списка истории.
Обновление 2017
Я думаю, что разработка на обеих вышеперечисленных структурах уже не работает. Другие рамки, которые находятся в стадии разработки:
И многие другие (я не буду предлагать полный список здесь).
Связанные
Ответ 2
Вот как я это делаю:
(В этом примере я создал два документа FXML со своими соответствующими контроллерами. Они называются FXMLLogin.fxml и Home.fxml соответственно).
Итак, чтобы перейти от FXMLLogin к Home,
В этом примере я создал метод внутри FXMLLoginController, который отвечает на кнопку "login" на этой форме:
@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 чрезвычайно важен.
Если я правильно понял ваш вопрос, тогда это должно сделать трюк.
Переключение между панелями совершенно не очевидно и не ясно изложено в любом учебнике в Интернете, которое я нашел. Мне пришлось много раз заниматься рекламой, прежде чем я понял это впервые. К счастью, это на самом деле довольно просто, как только вы получите его.
Надеюсь, я не понял ваш вопрос неправильно? Дайте мне знать, если это вам нужно:)
Ответ 3
JRebirth Application Framework предоставляет пользовательский 'CardLayout', используя свой выделенный шаблон wB-CSMvc.
StackModel класс выполнит задание (предоставлено org.jrebirth.af:компонентным артефактом), вы можете найти 2 варианта использования здесь и здесь.
Каждая модель "карточки" может быть вызвана с использованием идентификатора enum | modelKey, и каждый стек имеет уникальное имя.
Первый образец используется для JRebirth Demo Application, это довольно простое приложение, которое позволит отображать другие приложения витрины 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) и поместит ее корень node в корень сцены node (StakPane, автоматически построенный).
В MainModel будут перечислены все вспомогательные модули приложения, чтобы добавить записи кнопок в их левое меню и StackModel, которые будут отображать содержимое каждого модуля.
StackModel загружается с помощью специальной аннотации, используя свой уникальный строковый ключ.
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 в качестве внутреннего компонента, и каждая карта будет идентифицирована записью enum (сохраненной в FXMLPage), посмотрим FXMLShowCaseModel:
final InnerComponent<StackModel> stack = CBuilder.innerComponent(StackModel.class, FXMLPage.class);
this.stackModel = findInnerComponent(stack);
Перечисление, связанное с вводом ссылки с помощью модели:
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));
}
}
Сообщите мне, если у вас есть вопрос