JavaFX progress indicator stop spinning when heavy load runs

萝らか妹 提交于 2020-08-11 05:41:28

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!