1

Is there any possibility to show a (spinning, not stuck) progress indicator while theres some heavy lifting done in javafx ui thread?

Backstory: Ive got to load a rather big tableView into a TitledPane (which takes multiple seconds). The command that makes the whole thing lag is TitledPane.setContent(myTableView). So i want to at least show some loading animation or indicator during this time.

So right now im doing the following, to give the ui thread enough room to breath to show my loading animation before doing the expensive call:

showMyLoadingAnimation();
PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(event -> {
    setContent(myTableView);
});
pause.play();
dismissMyLoadingAnimation();

this works get the progress indicator shown, but once the ui thread starts working on setContent its getting stuck.

ive seen some people using swing to display a layover or dialog with the animation just to have it running in a different thread and not get stuck, but couldnt find a convincing solution yet.

EDIT: Ok, i found that root of the problem. so ive got tables prefHeightProperty bound to the size of the list its displaying as shown here: JavaFX - Adapt TableView height to number of rows did that weeks ago, worked, forgot about it.

that costs a LOT of time. so although timing the call when setting the binding doesnt show that it takes a lot of time, the ui thread seems have a massive problem with it when the tables are getting big.

thanks to james, his answer will be the correct one for most people running into this kind of issue.

can someone please comment this issue over there: JavaFX - Adapt TableView height to number of rows (since this is first question i dont have enough rep. yet to comment)

Community
  • 1
  • 1
84n4n4
  • 13
  • 5
  • `titledPane.setContent(myTableView);` cannot realistically be taking any appreciable time. Something else that is happening in the code is taking the time; probably loading the data. You should move the time-consuming work (which is not the actual UI work) to a background thread. – James_D Feb 10 '17 at 14:57

1 Answers1

4

Load the data in a Task, running in a background thread. Display the loading animation (or ProgressIndicator, etc) when you start the task, and remove it when the task finishes.

The basic idea looks like:

TableView<MyDataType> table = new TableView<>();
// set up columns...

Task<List<MyDataType>> loadDataTask = new Task<List<MyDataType>>() {
    @Override
    protected List<MyDataType> call() throws Exception {
        List<MyDataType> data = ... ;
        // load data and populate list ...
        return data ;
    }
};
loadDataTask.setOnSucceeded(e -> table.getItems().setAll(loadDataTask.getValue()));
loadDataTask.setOnFailed(e -> { /* handle errors... */ });

ProgressIndicator progressIndicator = new ProgressIndicator();
table.setPlaceHolder(progressIndicator);

Thread loadDataThread = new Thread(loadDataTask);
loadDataThread.start();
James_D
  • 201,275
  • 16
  • 291
  • 322
  • hi! ive already been there. but i tried again with your code but no avail. preparing data isnt a big issue, its already there, ive found that the table.getItems().set... doesnt take that long overall. now after digging deeper i found the culprit of this mess, ive been adjusting the table height to fit the contents with this http://stackoverflow.com/questions/27945817/javafx-adapt-tableview-height-to-number-of-rows via binding the prefHeightProperty of the table to the size of the list. the call itself isnt taking that long (tried timing it), but if i comment it out its suddenly fast. – 84n4n4 Feb 10 '17 at 15:47
  • @84n4n4 I don't understand the comment about how long `table.getItems().setAll(...)` takes. I am not running `getItems().setAll(...)` in the background thread, I am running it on the FX application thread, after the background thread completes. And obviously, even if you are binding the height of the table to the size of the items list (which tbh I don't think is robust enough a solution to use), if you only change the list once (with `setAll(...)`) as opposed to adding each element at a time, that isn't a problem either. Sounds like you need to figure out what the actual issue is first. – James_D Feb 10 '17 at 15:53
  • sorry for the confusion. preparing data is not a timing issue. setting the data in the table is not a timing issue. setting an empty or small table in my pane not a timing issue. but when the having a lot of data the whole thing breaks. >>if you only change the list once (with setAll(...)) as opposed to adding each element at a time, that isn't a problem either. this is exactly what im doing, with the binding of prefHeight its slow, without its fast. i will hack up a small exampe and try to replicate and post it here. – 84n4n4 Feb 10 '17 at 16:29
  • @84n4n4 I guess if you are causing the `prefHeight` to be huge, you end up forcing the table to create a large number of cells... Presumably this would break the layout in other ways, though, and you wouldn't need that solution for a large table anyway. – James_D Feb 10 '17 at 16:33
  • yes, exactly this. the layout however works since i have an Accordion inside aScrollPane. and the TitledPanes are again the ones containing the table(s). so i wanted to get around having two scrollbars on the side, so this is why i ended up with the height binding. (yes, i know, dont worry, im not gonna do this again next time) thanks a lot for your time. – 84n4n4 Feb 10 '17 at 17:41