如何验证进度指示器的 setVisible 属性
How to validate setVisible property for progress indicator
我正在编写一个 javafx 程序,我需要在登录系统后放置一个进度指示器,它应该等待特定的时间,直到它从服务器加载所需的对象。
我是 javafx 的新手,我想要一种方法来验证控制器中的 setVisible(boolean) 属性,在初始化时它应该是不可见的,并且在初始化方法中将其设置为 false 没有问题,但在初始化之后在控制器中我我想我应该验证更改。
有没有一种方法可以用来验证此 属性 更改?
//////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中通常无效,加上解决线程问题
解释你原来问题的一些问题以及为什么这个答案似乎不能直接回答它:
- 您通常不必
validate()
(i.e. force the layout of the subcomponents) components in JavaFX, as JavaFX will do that for you automatically each pulse(研究链接文档以更全面地理解这一点)。
- 有时您可能想要 generate a layout pass(我想这在概念上有点类似于显式调用以验证 JFrame)。但是,这通常只是因为您想测量某个东西的尺寸,一旦它已经应用 css 并且已经完全布置(这不是您想要在这里做的)。
- 您不应尝试在场景图中更改 JavaFX 应用程序线程之外的内容,因为这会导致异常和竞争条件(即您在您创建的线程中的进度指示器上调用
setVisible
错了)。
- 一些并发任务最好使用built-in JavaFX concurrency mechanisms,例如提交登录到登录服务,根据任务进度和结果与UI接口
该怎么做
我认为你想做的是:
- 创建登录提示,其中登录逻辑发生在另一个线程中
- 在另一个线程中进行登录时,进度指示器会无限期地旋转。
- 进度指示器仅在登录过程中显示,一旦登录尝试完成(无论成功与否)都不会显示。
示例应用程序
登录 Service 处理异步登录过程。登录窗格中的进度指示器指示登录正在进行。登录完成后,登录窗格将替换为已登录用户的应用程序窗格。
以下行确保进度指示器仅在登录服务执行时显示:
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 程序,我需要在登录系统后放置一个进度指示器,它应该等待特定的时间,直到它从服务器加载所需的对象。 我是 javafx 的新手,我想要一种方法来验证控制器中的 setVisible(boolean) 属性,在初始化时它应该是不可见的,并且在初始化方法中将其设置为 false 没有问题,但在初始化之后在控制器中我我想我应该验证更改。 有没有一种方法可以用来验证此 属性 更改?
//////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中通常无效,加上解决线程问题
解释你原来问题的一些问题以及为什么这个答案似乎不能直接回答它:
- 您通常不必
validate()
(i.e. force the layout of the subcomponents) components in JavaFX, as JavaFX will do that for you automatically each pulse(研究链接文档以更全面地理解这一点)。 - 有时您可能想要 generate a layout pass(我想这在概念上有点类似于显式调用以验证 JFrame)。但是,这通常只是因为您想测量某个东西的尺寸,一旦它已经应用 css 并且已经完全布置(这不是您想要在这里做的)。
- 您不应尝试在场景图中更改 JavaFX 应用程序线程之外的内容,因为这会导致异常和竞争条件(即您在您创建的线程中的进度指示器上调用
setVisible
错了)。 - 一些并发任务最好使用built-in JavaFX concurrency mechanisms,例如提交登录到登录服务,根据任务进度和结果与UI接口
该怎么做
我认为你想做的是:
- 创建登录提示,其中登录逻辑发生在另一个线程中
- 在另一个线程中进行登录时,进度指示器会无限期地旋转。
- 进度指示器仅在登录过程中显示,一旦登录尝试完成(无论成功与否)都不会显示。
示例应用程序
登录 Service 处理异步登录过程。登录窗格中的进度指示器指示登录正在进行。登录完成后,登录窗格将替换为已登录用户的应用程序窗格。
以下行确保进度指示器仅在登录服务执行时显示:
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;
}
};
}
}
}