JavaFx ProgressIndicator while Loading Pane (GUI)

眉间皱痕 提交于 2019-12-04 02:30:50

问题


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?


回答1:


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();



回答2:


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?



来源:https://stackoverflow.com/questions/35081031/javafx-progressindicator-while-loading-pane-gui

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