简体   繁体   中英

java swing timer: texts not updating

It is the first time that I use the java swing timer. My goal is to update the label once per sec, but when I run the program, an error occures. To make sure that it justs bugs with the labels, I tried to insert a progressbar that should get slowly filled, and it worked. How am i supposed to do?

This is my main class:

package Application;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class main extends Application {

    @Override
    public void start(Stage primaryStage) throws IOException {
        Parent root =   FXMLLoader.load(getClass().getResource("/Interface/interface.fxml"));
        Scene scene = new Scene (root);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Here is the main controller of my fxml document:

package Application;

import java.awt.event.ActionListener;
import java.net.URL;
import java.util.ResourceBundle;

import javax.swing.Timer;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;

public class MainController implements Initializable {

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        // TODO Auto-generated method stub

    }

    @FXML
    Label label;
    double count = 0.0;

    public void ok(ActionEvent event){
        ActionListener action = new ActionListener(){

            @Override
            public void actionPerformed(java.awt.event.ActionEvent arg0) {
                count+=0.1;
                label.setText(count+"");
            }
        };

        Timer timer = new Timer(1000, action);
        timer.start();
    }
}

That is it. I got a button that is supposed to start the Timer "timer" that is supposed to change the Label "label". The method "ok" is the one called when the button is pressed. And, as I already said, it is supposed to start the timer. But when i do so, i get an error:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
    at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
    at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(BehaviorSkinBase.java:197)
    at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
    at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
    at javafx.scene.control.Labeled.setText(Labeled.java:145)
    at Application.MainController$1.actionPerformed(MainController.java:46)
    at javax.swing.Timer.fireActionPerformed(Unknown Source)
    at javax.swing.Timer$DoPostEvent.run(Unknown Source)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$500(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

What can i do?

Similar to Swing, there is a special thread that you need to run "inside" to do your UI updates - the Java FX event dispatch thread, which handles all GUI-related tasks.

You need to use Platform.runLater() here.

In other words: you need to define a Runnable that makes the update; and then you pass an instance of that Runnable to the afore mentioned runLater() .

You error message is basically telling you that FX doesn't want you to make updates in any other thread!

Besides: it shouldn't directly matter, but probably you better use java.util.Timer instead of java.Swing.Timer .

EDIT: you need something like this; assuming that you want things to be triggered by your ok() method:

public void ok(ActionEvent event){
    final Runnable update = new Runnable() {
       @Override 
       public void run() {
         count+=0.1;
         label.setText(count+"");
        }
    };

   ActionListener delayedAction = new ActionListener(){
     @Override
     public void actionPerformed(java.awt.event.ActionEvent arg0) {
       Platform.runLater(update);
     }
   };

   new Timer(1000, delayedAction).start();
}

    Platform.runLater(update);
}

The above does:

A) create a Runnable object that will update your UI element

B) use the "known" Swing Timer to invoke that Runnable using the required Platform.runLater() call

That should work, but might require some fine tuning (code is neither compiled nor tested).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM