JavaFx ProgressIndicator while Loading Pane (GUI)

2019-02-20 19:45发布

In my application, I have to build big panes with a lot of content. I will show a ProgressIndicator while the GUI is loading.

My first test, I will show a ProgressIndicator while I adding a lot of tabs into a TabPane.

That's my test Code:

public class SampleController implements Initializable {
    private TabPane tabPane;
    @FXML
    private BorderPane borderPane;

    ProgressIndicator myProgressIndicator;
    Task<Void> myLongTask;


    @Override
    public void initialize(URL location, ResourceBundle resources)
    {
        myProgressIndicator = new ProgressIndicator();
        Pane p1 = new Pane(myProgressIndicator);
        tabPane = new TabPane();
        Pane p2 = new Pane(tabPane);


        myLongTask = new Task<Void>()
        {
        @Override
        protected Void call() throws Exception
        {
        for (int i = 1; i < 1000; i++)
        {
//          Thread.sleep(10);
            Tab newTab = new Tab("Number:" + i);
            tabPane.getTabs().add(newTab);
        }
        return null;
        }
        };
        borderPane.centerProperty().bind(Bindings.when(myLongTask.runningProperty()).then(p1).otherwise(p2));

        new Thread(myLongTask).start();
    }
}

But the application will show the window if the Task has finished. If I replace the lines inside the for-loop with Thread.sleep(10) the application show the Indicator and, after all, sleep, it shows the GUI.

How can I show an Indicator while the GUI is not loaded already?

2条回答
狗以群分
2楼-- · 2019-02-20 19:53

You have a Task that creates a result (i.e. a TabPane). Therefore it's more convenient to use TabPane as type parameter instead of Void also you should call updateProgress to update the progress property and bind that property to the progress property of the ProgressIndicator.

The result can be added to the BorderPane in the onSucceded handler instead of creating a (more or less) complicated binding:

Task<TabPane> myLongTask;

@Override
public void initialize(URL url, ResourceBundle rb) {

    myLongTask = new Task<TabPane>() {

        @Override
        protected TabPane call() throws Exception {
            TabPane tabPane = new TabPane();
            List<Tab> tabs = tabPane.getTabs();
            final int count = 1000 - 1;
            for (int i = 1; i <= count; i++) {
                Thread.sleep(10);
                Tab newTab = new Tab("Number:" + i);
                tabs.add(newTab);
                updateProgress(i, count);
            }
            return tabPane;
        }
    };
    myLongTask.setOnSucceeded(evt -> {
        // update ui with results
        tabPane = myLongTask.getValue();
        borderPane.setCenter(new Pane(tabPane));
    });

    // add progress indicator to show progress of myLongTask
    myProgressIndicator = new ProgressIndicator();
    myProgressIndicator.progressProperty().bind(myLongTask.progressProperty());
    borderPane.setCenter(new Pane(myProgressIndicator));

    new Thread(myLongTask).start();
}

Simply creating the tabs is fast however, and you won't see any progress indicator in the UI. Layouting a TabPane with 999 Tabs however is rather slow. The UI will most likely freeze for a short time. You can work around this by adding only a limited number of Tabs in each frame:

Return a List<Tab> from the task instead of a TabPane; these Tabs should not be added to the TabPane (yet). You can use a AnimationTimer to add a fixed number of tabs each frame:

final List<Tab> result = ...; // your tab list

// number of elements added each frame
final int step = 5;

final int size = result.size();
AnimationTimer timer = new AnimationTimer() {

    int index = 0;

    @Override
    public void handle(long now) {
        tabPane.getTabs().addAll(result.subList(index, Math.min(size, index+step)));
        index += step;
        if (index >= size) {
            this.stop();
        }
    }
};
timer.start();
查看更多
女痞
3楼-- · 2019-02-20 20:14

I have change the class like this:

public class SampleController implements Initializable {
@FXML
private BorderPane borderPane;
ProgressIndicator myProgressIndicator;
Task<List<Tab>> myLongTask;
TabPane tabPane = new TabPane();

@Override
public void initialize(URL location, ResourceBundle resources)
{
        myLongTask = new Task<List<Tab>>()
        {
            @Override
            protected List<Tab> call() throws Exception
            {
            List<Tab> newTabs = new ArrayList<Tab>();
            final int count = 1000 - 1;
            for (int i = 1; i <= count; i++)
            {
                Tab newTab = new Tab("Number:" + i);
                newTabs.add(newTab);
            }
            return newTabs;
            }
        };

    myProgressIndicator = new ProgressIndicator();
    myProgressIndicator.progressProperty().bind(myLongTask.progressProperty());
    borderPane.setCenter(new Pane(myProgressIndicator));

    new Thread(myLongTask).start();

    myLongTask.setOnSucceeded(evt -> {
    final List<Tab> result = myLongTask.getValue();
    final int step = 5;
    final int size = result.size();

    AnimationTimer timer = new AnimationTimer() {
        int index = 0;
        @Override
        public void handle(long now) {
        tabPane.getTabs().addAll(result.subList(index, Math.min(size, index+step)));
        index += step;
        if (index >= size) {
            this.stop();
        }
        }
    };
        timer.start();
        borderPane.setCenter(new Pane(tabPane));
    });
}

}

Is it this what you mean?

查看更多
登录 后发表回答