问题
I am trying to implement busy indicator using ProgressIndicator. But when the heavy load starts the indicator freezes. A sample code is shown below.
import javafx.beans.value.ChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class BusyIcon {
private static Stage busyWindow;
public static void showBusyIcon(final Stage stage) {
busyWindow = new Stage(StageStyle.UNDECORATED);
//busyWindow.setOpacity(.3);
busyWindow.initOwner(stage);
busyWindow.initModality(Modality.WINDOW_MODAL);
StackPane stackPane = new StackPane();
final ProgressIndicator loadingIndicator = new ProgressIndicator();
loadingIndicator.setVisible(true);
stackPane.getChildren().add(loadingIndicator);
Scene scene = new Scene(stackPane, 100, 100);
scene.setFill(Color.TRANSPARENT);
busyWindow.setScene(scene);
ChangeListener<Number> widthListener = (observable, oldValue, newValue) -> {
double stageWidth = newValue.doubleValue();
busyWindow.setX(stage.getX() + stage.getWidth() / 2 - stageWidth / 2);
};
ChangeListener<Number> heightListener = (observable, oldValue, newValue) -> {
double stageHeight = newValue.doubleValue();
busyWindow.setY(stage.getY() + stage.getHeight() / 2 - stageHeight / 2);
};
busyWindow.widthProperty().addListener(widthListener);
busyWindow.heightProperty().addListener(heightListener);
busyWindow.setOnShown(e -> {
busyWindow.widthProperty().removeListener(widthListener);
busyWindow.heightProperty().removeListener(heightListener);
});
busyWindow.show();
}
public static void closeBusyIcon(final Stage stage) {
if (busyWindow != null) {
busyWindow.close();
busyWindow = null;
}
}
}
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import preloader.BusyIcon;
public class QuestionExample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Task Progress Tester");
StackPane testPane = new StackPane();
Button b = new Button("Load");
b.setOnAction((event) -> {
BusyIcon.showBusyIcon(primaryStage);
Task t = new Task() {
@Override
protected Object call() throws Exception {
try {
addNewComponent(testPane);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
};
t.setOnSucceeded((ev) -> {
BusyIcon.closeBusyIcon(primaryStage);
});
new Thread(t).start();
});
testPane.getChildren().add(b);
primaryStage.setScene(new Scene(testPane, 300, 250));
primaryStage.show();
}
private void addNewComponent(Pane testPane) {
try {
/**
* Some heavy load work will run here
*/
Thread.sleep(2000);
Platform.runLater(() -> {
try {
/**
* We need to change the fx controls here
*/
Button b1 = new Button("New Component");
testPane.getChildren().add(b1);
/**
* This may take some time
*/
Thread.sleep(2000);
} catch (Exception ex) {
ex.printStackTrace();
}
});
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
BusyIcon is used for showing progress indicator. If we are not using the Platform.runLater then it will throw 'Not in FX thread' exception will be thrown.
回答1:
I suggest you try ControlsFX MaskerPane. The key is to set the MaskerPane
visible and move it to the front of an AnchorPane
before the task runs. When the task finishes, set it invisible and move it to the back of the AnchorPane
.
DEMO:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.control.MaskerPane;
/**
*
* @author blj0011
*/
public class MaskerPaneTest extends Application
{
@Override
public void start(Stage primaryStage)
{
MaskerPane mpDeterminate = new MaskerPane();
MaskerPane mpUndeterminate = new MaskerPane();
mpDeterminate.setVisible(false);
mpUndeterminate.setVisible(false);
Button btn = new Button();
btn.setText("Determinate");
btn.setOnAction((ActionEvent event) -> {
mpDeterminate.setVisible(true);
mpDeterminate.toFront();
Task<Void> task = new Task<Void>()
{
@Override
protected Void call() throws Exception
{
for (int i = 0; i < 40000000; i++) {
//Do something
updateProgress(i, 40000000);
}
return null;
}
};
mpDeterminate.progressProperty().bind(task.progressProperty());
task.setOnSucceeded((workerStateEvent) -> {
mpDeterminate.setVisible(false);
mpDeterminate.toBack();
});
new Thread(task).start();
});
Button btn2 = new Button();
btn2.setText("Undeterminate");
btn2.setOnAction((ActionEvent event) -> {
mpUndeterminate.setVisible(true);
mpUndeterminate.toFront();
Task<Void> task = new Task<Void>()
{
@Override
protected Void call() throws Exception
{
for (int i = 0; i < 100000; i++) {
//Do something
System.out.println("working!");
}
return null;
}
};
mpUndeterminate.progressProperty().bind(task.progressProperty());
task.setOnSucceeded((workerStateEvent) -> {
mpUndeterminate.setVisible(false);
mpUndeterminate.toBack();
});
new Thread(task).start();
});
StackPane root = new StackPane(mpDeterminate, mpUndeterminate, new VBox(btn, btn2));
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
来源:https://stackoverflow.com/questions/55281614/javafx-progress-indicator-stop-spinning-when-heavy-load-runs