Java 如何验证进度指示器的setVisible属性

Java 如何验证进度指示器的setVisible属性,java,javafx,javafx-2,fxml,progress-indicator,Java,Javafx,Javafx 2,Fxml,Progress Indicator,我正在写一个javafx程序,我需要在登录系统后放置一个进度指示器,它应该等待特定的时间,直到它从服务器加载所需的对象。 我是javafx新手,我想要一种从控制器验证setVisible(boolean)属性的方法,在initialize中它应该是不可见的,在initialize方法中将它设置为false没有问题,但在controller中初始化之后,我想我应该验证更改。 是否有一种方法可用于验证此属性更改 //////pseudocode @FXML ProgressIndicato

我正在写一个javafx程序,我需要在登录系统后放置一个进度指示器,它应该等待特定的时间,直到它从服务器加载所需的对象。 我是javafx新手,我想要一种从控制器验证setVisible(boolean)属性的方法,在initialize中它应该是不可见的,在initialize方法中将它设置为false没有问题,但在controller中初始化之后,我想我应该验证更改。 是否有一种方法可用于验证此属性更改

     //////pseudocode
@FXML ProgressIndicator pi;
public void initialize(...){
 PI.setVisible(false); 
  }
@FXML public void buttonOnClick(){
Thread t1=new Thread(new Runnable(....)) // A thread to load data
 t1.start
pi.setVisible(true); //HERE IS MY PROBLEM
Thread t2;//a thread to increase progress
  t2.start();
t1.join();
}

为什么
validate()
的概念在JavaFX中通常无效,再加上解决线程问题

要解释原始问题的一些问题,以及为什么此答案似乎无法直接回答:

  • 您通常不必在JavaFX中使用(即强制子组件的布局)组件,因为JavaFX会自动为您这样做(研究链接文档以更全面地理解这一点)
  • 有时您可能想要(我想,这在概念上与JFrame的显式验证调用有些相似)。但是,这通常只是因为你想测量某个东西的维度,一旦它应用了css并完全布局好(这不是你想在这里做的)
  • 您不应该尝试在JavaFX应用程序线程之外更改场景图中的内容,因为这将导致异常和竞争条件(即,您在创建的线程中对进度指示器上的
    setVisible
    的调用是错误的)
  • 最好用于一些并发任务,例如提交登录到登录服务,并根据任务进度和结果与UI交互
  • 该怎么办

    我认为你想做的是:

  • 在另一个线程中出现登录逻辑时创建登录提示
  • 当登录在另一个线程中进行时,有一个不确定的进度指示器旋转
  • 进度指示器仅在登录过程中显示,并且在登录尝试完成后不显示(无论成功与否)
  • 示例应用程序

    登录处理异步登录过程。登录窗格中的进度指示器指示登录正在进行。登录完成后,登录窗格将替换为登录用户的应用程序窗格

    以下行确保仅在登录服务执行时显示进度指示器:

    progressIndicator.visibleProperty().bind(loginService.runningProperty());
    
    完整代码:

    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.concurrent.Service;
    import javafx.concurrent.Task;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.layout.*;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    
    public class LoginServiceApp extends Application {
        private LoginService loginService = new LoginService();
    
        @Override
        public void start(Stage stage) throws IOException {
            Pane loginPane = createLoginPane();
            loginService.setOnSucceeded(event ->
                    stage.getScene().setRoot(createAppPane(stage))
            );
    
            stage.setScene(new Scene(new StackPane(loginPane)));
            stage.show();
        }
    
        private Pane createLoginPane() {
            GridPane credentialsGrid = new GridPane();
            credentialsGrid.setHgap(10);
            credentialsGrid.setVgap(10);
            TextField usernameField = new TextField("frobozz");
            PasswordField passwordField = new PasswordField();
            credentialsGrid.addRow(0, new Label("Username"), usernameField);
            credentialsGrid.addRow(1, new Label("Password"), passwordField);
    
            Button loginButton = new Button("Login");
            loginButton.setOnAction(event -> {
                loginService.setUsername(usernameField.getText());
                loginService.setPassword(passwordField.getText());
                loginService.restart();
            });
            loginButton.disableProperty().bind(loginService.runningProperty());
    
            ProgressIndicator progressIndicator = new ProgressIndicator();
            progressIndicator.visibleProperty().bind(loginService.runningProperty());
            progressIndicator.setPrefSize(20, 20);
    
            HBox loginControl = new HBox(10, loginButton, progressIndicator);
            VBox loginPane = new VBox(10, credentialsGrid, loginControl);
            loginPane.setPadding(new Insets(10));
    
            return loginPane;
        }
    
        private Pane createAppPane(Stage stage) {
            Button logoutButton = new Button("Logout");
            logoutButton.setOnAction(event -> stage.getScene().setRoot(createLoginPane()));
            HBox appPane = new HBox(logoutButton);
            appPane.setPadding(new Insets(10));
    
            return appPane;
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
        private static class LoginService extends Service<Void> {
            private StringProperty username = new SimpleStringProperty(this, "username");
            public final void setUsername(String value) { username.set(value); }
            public final String getUsername() { return username.get(); }
            public final StringProperty usernameProperty() { return username; }
    
            private StringProperty password = new SimpleStringProperty(this, "password");
            public final void setPassword(String value) { password.set(value); }
            public final String getPassword() { return password.get(); }
            public final StringProperty passwordProperty() { return password; }
    
            @Override
            protected Task<Void> createTask() {
                final String _username = getUsername();
                final String _password = getPassword();
                return new Task<Void>() {
                    @Override
                    protected Void call() throws Exception {
                        // Simulate a long call to a login service,
                        // using the username and password we saved when the task was created.
                        // If login fails, an exception can be raised to report it and the
                        // caller starting the service can monitor setOnException to handle it.
                        // Or the Task could return a result value instead of void and the caller
                        // could monitor the value property of the task in addition to the exception handler.
                        Thread.sleep(1_000);
                        return null;
                    }
                };
            }
        }
    }
    
    导入javafx.application.application;
    导入javafx.beans.property.SimpleStringProperty;
    导入javafx.beans.property.StringProperty;
    导入javafx.concurrent.Service;
    导入javafx.concurrent.Task;
    导入javafx.geometry.Insets;
    导入javafx.scene.scene;
    导入javafx.scene.control.*;
    导入javafx.scene.layout.*;
    导入javafx.stage.stage;
    导入java.io.IOException;
    公共类LoginServiceApp扩展应用程序{
    private LoginService LoginService=new LoginService();
    @凌驾
    public void start(Stage)引发IOException{
    窗格loginPane=createLoginPane();
    loginService.setOnSucceed(事件->
    stage.getScene().setRoot(createAppPane(stage))
    );
    舞台场景(新场景(新StackPane(loginPane));
    stage.show();
    }
    专用窗格createLoginPane(){
    GridPane credentialsGrid=新建GridPane();
    证书网格。setHgap(10);
    证书网格。setVgap(10);
    TextField usernameField=新的TextField(“泡沫”);
    PasswordField PasswordField=新PasswordField();
    addRow(0,新标签(“用户名”)、用户名字段);
    credentialsGrid.addRow(1,新标签(“密码”),密码字段);
    按钮登录按钮=新按钮(“登录”);
    loginButton.setOnAction(事件->{
    loginService.setUsername(usernameField.getText());
    loginService.setPassword(passwordField.getText());
    loginService.restart();
    });
    loginButton.disableProperty().bind(loginService.runningProperty());
    ProgressIndicator ProgressIndicator=新的ProgressIndicator();
    progressIndicator.visibleProperty().bind(loginService.runningProperty());
    progressIndicator.setPrefSize(20,20);
    HBox loginControl=新HBox(10,登录按钮,进度指示器);
    VBox loginPane=新的VBox(10,凭证网格,登录控制);
    loginPane.setPadding(新插图(10));
    返回登录页;
    }
    专用窗格createAppPane(阶段){
    按钮注销按钮=新按钮(“注销”);
    logoutButton.setOnAction(事件->stage.getScene().setRoot(createLoginPane());
    HBox appPane=新的HBox(注销按钮);
    appPane.setPadding(新插图(10));
    返回应用程序窗格;
    }
    公共静态void main(字符串[]args){
    发射(args);
    }
    私有静态类LoginService扩展服务{
    私有StringProperty用户名=新的SimpleStringProperty(此“用户名”);
    公共最终void setUsername(字符串值){username.set(值);}
    公共最终字符串getUsername(){返回username.get();}
    公共最终StringProperty usernameProperty(){返回用户名;}
    private StringProperty password=新的SimpleStringProperty(此“密码”);
    public final void setPassword(字符串值){password.set(值);}
    公共最终字符串getPassword(){return password.get();}
    公共最终StringProperty passwordProperty(){返回密码;}
    @凌驾
    受保护的任务createTask(){
    最后一个字符串_username=getUsername();
    最后一个字符串_password=getPassword();
    返回新任务(){
    @凌驾
    受保护的Void调用()抛出