如何在单个阶段中更改动态生成的JavaFX场景?

如何在单个阶段中更改动态生成的JavaFX场景?,javafx,scene,Javafx,Scene,我目前正在做一个项目,要求我在场景之间来回切换。我已经为它编写了一些代码,但它并没有我想要的那么优雅,特别是当我在它们之间切换时,几乎没有闪烁,有时甚至我生成的按钮也会消失,只是在生成另一个场景时再次出现。此外,我在应用程序中使用的布局并不是固定的,我认为使用FXML可能不适合我正在做的事情 多谢各位 这就是我用来在场景之间切换的内容: void changeScene(Stage stage,Scene scene){ stage.setScene(scene); primar

我目前正在做一个项目,要求我在场景之间来回切换。我已经为它编写了一些代码,但它并没有我想要的那么优雅,特别是当我在它们之间切换时,几乎没有闪烁,有时甚至我生成的按钮也会消失,只是在生成另一个场景时再次出现。此外,我在应用程序中使用的布局并不是固定的,我认为使用FXML可能不适合我正在做的事情

多谢各位

这就是我用来在场景之间切换的内容:

void changeScene(Stage stage,Scene scene){
    stage.setScene(scene);
    primaryStage.setFullScreen(true);
}
我假设“在场景之间切换”是指您希望更改现有窗口的全部内容

有两种(非常)略有不同的方法可以做到这一点。创建一个新的
场景
并将其传递给
阶段
设置场景(…)
方法。或者创建作为新UI根的
Parent
(通过FXML或其他方式),并将其传递给现有
场景的
setRoot(…)
方法。我看不出两者之间有什么真正的优势

这里是第二个选项的最小实现。UI与问题无关:重要的部分是“登录”按钮(从登录场景切换到主场景)和“注销”按钮(切换回)的事件处理程序

import java.util.stream.IntStream;
导入javafx.application.application;
导入javafx.beans.binding.Bindings;
导入javafx.beans.property.IntegerProperty;
导入javafx.beans.property.SimpleIntegerProperty;
导入javafx.geometry.HPos;
导入javafx.geometry.Insets;
导入javafx.geometry.Pos;
导入javafx.scene.Parent;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.control.Label;
导入javafx.scene.control.ListView;
导入javafx.scene.control.SplitPane;
导入javafx.scene.control.TextField;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.ColumnConstraints;
导入javafx.scene.layout.GridPane;
导入javafx.scene.layout.Priority;
导入javafx.scene.text.Font;
导入javafx.scene.text.FontWeight;
导入javafx.stage.stage;
公共类最小场景切换示例扩展应用程序{
@凌驾
公共无效开始(阶段primaryStage){
LoginView LoginView=新LoginView();
场景=新场景(loginView.getView(),400400);
初级阶段。场景(场景);
primaryStage.show();
}
公共静态类LoginView{
私有最终IntegerProperty登录尝试;
私有最终网格窗格视图;
公共登录视图(){
视图=新建网格窗格();
TextField usernameTF=新的TextField(“用户”);
TextField passwordTF=新的TextField(“pass”);
//登录按钮切换到主视图:
按钮登录按钮=新按钮(“登录”);
loginButton.setOnAction(事件->{
if(usernameTF.getText().equalsIgnoreCase(“用户”)
&&passwordTF.getText().equalsIgnoreCase(“pass”)){
//***切换到主视图:***
父mainView=新的mainView().getView();
view.getScene().setRoot(mainView);
}否则{
loginattents.set(loginattents.get()+1);
}
});
//只需设置登录UI…与此示例无关:
LoginAttents=新的SimpleIntegerProperty();
usernameTF.setPrompText(“提示:用户”);
passwordTF.setPrompText(“提示:通过”);
view.addRow(0,新标签(“用户名:”),usernameTF);
view.addRow(1,新标签(“密码”)、passwordTF;
Label loginErrorMessage=新标签();
loginErrorMessage.textProperty().bind(
Bindings.when(loginattests.isEqualTo(0))
.然后(“”)
。否则(Bindings.format(“登录错误(尝试次数:%d)”,
登录测试);
添加(loginErrorMessage,0,2,2,1);
添加(登录按钮,0,3,2,1);
ColumnConstraints leftCol=新ColumnConstraints();
leftCol.setHgrow(Priority.NEVER);
左列设定值(HPos.右);
ColumnConstraints rightCol=新ColumnConstraints();
rightCol.setHgrow(优先级始终);
右列设定值(HPos左);
view.getColumnConstraints().addAll(leftCol,rightCol);
GridPane.setHalignment(loginErrorMessage,HPos.CENTER);
GridPane.setHalignment(登录按钮,HPos.CENTER);
视图。setHgap(10);
视图。setVgap(16);
视图。设置对齐(位置中心);
}
公共父getView(){
返回视图;
}
}
公共静态类MainView{
私有边框视图;
公共主视图(){
视图=新建边框窗格();
//***注销按钮切换回登录视图:***
按钮注销按钮=新按钮(“注销”);
注销按钮。设置操作(事件->
view.getScene().setRoot(新的LoginView().getView());
//任意用户界面,与本例无关:
SplitPane SplitPane=新的SplitPane();
ListView ListView=新建ListView();
IntStream.rangeClosed(1,10)
.mapToObj(整数::toString)
.map(“项目”::concat)
.forEach(listView.getItems()::添加);
Label bigLabel=新标签();
bigLabel.textProperty().bind(
listView.getSelectionModel().SelectEditeProperty();
setFont(Font.Font(“Verdana”,fontwweight.BOLD,18));
BorderPane.setAlignment(大标签,位置中心);
BorderPane.setMargin(
import java.util.stream.IntStream;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;

public class MinimalSceneSwitchingExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        LoginView loginView = new LoginView();
        Scene scene = new Scene(loginView.getView(), 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static class LoginView {

        private final IntegerProperty loginAttempts ;

        private final GridPane view ;

        public LoginView() {

            view = new GridPane();

            TextField usernameTF = new TextField("user");
            TextField passwordTF = new TextField("pass");

            // Login button switches to main view:
            Button loginButton = new Button("Login");
            loginButton.setOnAction(event -> {
                if (usernameTF.getText().equalsIgnoreCase("user") 
                    && passwordTF.getText().equalsIgnoreCase("pass")) {

                    // *** Switch to main view: ***

                    Parent mainView = new MainView().getView();
                    view.getScene().setRoot(mainView);

                } else {
                    loginAttempts.set(loginAttempts.get()+1);
                }
            });

            // just set up login UI... irrelevant to this example:


            loginAttempts = new SimpleIntegerProperty();

            usernameTF.setPromptText("Hint: user");
            passwordTF.setPromptText("Hint: pass");

            view.addRow(0, new Label("Username:"), usernameTF);
            view.addRow(1, new Label("Password:"), passwordTF);

            Label loginErrorMessage = new Label();

            loginErrorMessage.textProperty().bind(
                    Bindings.when(loginAttempts.isEqualTo(0))
                    .then("")
                    .otherwise(Bindings.format("Login incorrect (Attempts: %d)", 
                            loginAttempts)));

            view.add(loginErrorMessage, 0, 2, 2, 1);

            view.add(loginButton, 0, 3, 2, 1);

            ColumnConstraints leftCol = new ColumnConstraints();
            leftCol.setHgrow(Priority.NEVER);
            leftCol.setHalignment(HPos.RIGHT);
            ColumnConstraints rightCol = new ColumnConstraints();
            rightCol.setHgrow(Priority.ALWAYS);
            rightCol.setHalignment(HPos.LEFT);

            view.getColumnConstraints().addAll(leftCol, rightCol);
            GridPane.setHalignment(loginErrorMessage, HPos.CENTER);
            GridPane.setHalignment(loginButton, HPos.CENTER);

            view.setHgap(10);
            view.setVgap(16);

            view.setAlignment(Pos.CENTER);
        }

        public Parent getView() {
            return view ;
        }

    }

    public static class MainView {

        private BorderPane view ;

        public MainView() {

            view = new BorderPane();

            // *** logout button switches back to a login view: ***

            Button logoutButton = new Button("Log out");
            logoutButton.setOnAction(event -> 
                view.getScene().setRoot(new LoginView().getView()));

            // Arbitrary UI, irrelevant to this example:

            SplitPane splitPane = new SplitPane();
            ListView<String> listView = new ListView<>();
            IntStream.rangeClosed(1, 10)
                .mapToObj(Integer::toString)
                .map("Item "::concat)
                .forEach(listView.getItems()::add);

            Label bigLabel = new Label();
            bigLabel.textProperty().bind(
                    listView.getSelectionModel().selectedItemProperty());
            bigLabel.setFont(Font.font("Verdana", FontWeight.BOLD, 18));
            BorderPane.setAlignment(bigLabel, Pos.CENTER);
            BorderPane.setMargin(bigLabel, new Insets(10));

            Label details = new Label();
            details.textProperty().bind(
                Bindings.when(
                        listView.getSelectionModel().selectedItemProperty().isNull())
                    .then("")
                    .otherwise(Bindings.format("This is where you would display "
                        + "all sorts of details about %1$s. "
                        + "If %1$s were really a model object, you "
                        + "might have a GridPane displaying all its "
                        + "properties, for example.", 
                        listView.getSelectionModel().selectedItemProperty())));

            details.setWrapText(true);

            BorderPane detailsPane = new BorderPane(details, bigLabel, null, null, null);

            splitPane.getItems().addAll(listView, detailsPane);

            view.setCenter(splitPane);
            view.setBottom(logoutButton);

            BorderPane.setAlignment(logoutButton, Pos.CENTER);
            BorderPane.setMargin(logoutButton, new Insets(8));
            BorderPane.setMargin(splitPane, new Insets(16));
        }

        public Parent getView() {
            return view ;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}