Reputation: 65
I want to update a JavaFX ProgressBar defined in an FXML file by another class, initialized in a controller thread. Currently it just does not update.
test.fxml
<ProgressBar fx:id="progressBar" prefWidth="5000.0" progress="0.0">
<VBox.margin>
<Insets top="3.0" />
</VBox.margin>
</ProgressBar>
Controller.java
@FXML
public static ProgressBar progressBar = new ProgressBar(0);
MyMain main;
@FXML
private void handleStartWork() throws Exception {
new Thread() {
@Override
public void run() {
try {
main = new MyMain();
main.doIt();
} catch (final Exception v) {
// ...
}
}
}.start();
}
MyMain.java
public void doIt(){
while(...){
Platform.runLater(() -> PoCOverviewController.progressBar.setProgress((count / sum) * 100));
}
}
I already tried different versions in consideration of posts like:
I don't know if it's the right approach to make the ProgressBar static. I just did not want to pass the Object through the workflow.
Update (Xavier Lambros answer): Now i tried it with singleton but it's still not working:
Controller.java
@FXML
public ProgressBar progressBar = new ProgressBar(0);
private static Controller INSTANCE = new Controller();
public static Controller getInstance() {
return INSTANCE;
}
public ProgressBar getProgressBar() {
return progressBar;
}
MyMain.java
public void doIt(){
while(...){
Platform.runLater(() -> Controller.getInstance().getProgressBar()
.setProgress((count / sum) * 100));
}
}
Upvotes: 3
Views: 5372
Reputation: 209398
As noted in javafx 8 compatibility issues - FXML static fields, you cannot make a @FXML
-annotated field static
(and it makes no sense to do so: these fields are inherently properties of the specific controller instance).
To allow the doIt()
method access to the progress bar, you could just pass it directly as a parameter:
@FXML
public ProgressBar progressBar ;
MyMain main;
@FXML
private void handleStartWork() throws Exception {
new Thread() {
@Override
public void run() {
try {
main = new MyMain();
main.doIt(progressBar);
} catch (final Exception v) {
// ...
}
}
}.start();
}
and then
public void doIt(ProgressBar progressBar){
while(...){
Platform.runLater(() -> progressBar.setProgress((count / sum) * 100));
}
}
In some circumstances, it might not make sense for the Main
class to have a dependency on the JavaFX API. In that case you could just pass a function that updates the progress bar:
@FXML
public ProgressBar progressBar ;
MyMain main;
@FXML
private void handleStartWork() throws Exception {
new Thread() {
@Override
public void run() {
try {
main = new MyMain();
main.doIt(progressBar::setProgress);
} catch (final Exception v) {
// ...
}
}
}.start();
}
and
public void doIt(DoubleConsumer progressUpdate){
while(...){
Platform.runLater(() -> progressUpdate.accept((count / sum) * 100));
}
}
Note that you haven't shown what's happening in your while
loop: if you are submitting too many runnables to the FX Application Thread, you might "flood" it and prevent it from updating in a reasonable time. You might consider using a Task
, which has specific API for updating a progress field to which the progress bar's progress
property can be bound. If it's still not working, you should edit your question to include a MCVE.
Upvotes: 4
Reputation: 876
I don't think you can have ProgressBar static.
My way is to have an accessor on the ProgressBar inside your controller and init the controller like this :
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/YourController.fxml");
loader.load();
After, you can access your ProgressBar with :
loader.<YourController>getController().getProgressBar();
If you need, to access it in different classes, many other possibilities, one is to make a Singleton :
public class Singleton
{
private ProgressBar progressBar;
private Singleton()
{}
private static Singleton INSTANCE = new Singleton();
public static Singleton getInstance()
{
return INSTANCE;
}
public ProgressBar getProgressBar() {
return progressBar;
}
public ProgressBar setProgressBar() {
return progressBar;
}
}
To call it :
Singleton.getInstance().getProgressBar();
Upvotes: 0