【问题标题】:JavaFX ToggleButton get in and out of an infinite loopJavaFX ToggleButton 进入和退出无限循环
【发布时间】:2016-05-08 09:04:00
【问题描述】:

我想在 JavaFX 中创建一个切换按钮,它将执行一个循环,并且只有当用户再次单击该按钮时才会停止;激活时,循环将不断更改 TextField 中的文本...

这里是切换按钮的代码:

toggleButton.setOnAction(event -> {
    try {

        if(toggleButton.isSelected())
        {
            new Thread(
                    new Runnable() {
                        public void run() {
                            while(toggleButton.isSelected())
                            {
                                try {
                                    Thread.sleep(200);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }

                                double rand = Math.random();
                                textField.setText(rand);
                            }
                        }
                    }
                    ).start();
        }

        else if(!toggleButton.isSelected())
        {
            toggleButton.setSelected(false);
        }

    } catch (Exception ex) {
        ex.printStackTrace();
    }
});

好吧,上面的代码确实可以工作,但是......问题是,如果没有足够慢的循环速度,(Thread.sleep(200) 事情),大量错误将打印在控制台中...但我需要它尽可能快...

下面是一个使用 Thread.sleep(200)没有的输出示例:

(无法粘贴所有内容,因为它太长了……但这里是完整输出的链接:http://pastebin.com/SZfugk3d

[sts] -----------------------------------------------------
[sts] Starting Gradle build for the following tasks: 
[sts]      run
[sts] -----------------------------------------------------
:compileJava
:compileRetrolambdaMain
:processResources UP-TO-DATE
:classes
:compileAndroidJava SKIPPED
:compileRetrolambdaAndroid SKIPPED
:compileTestJava UP-TO-DATE
:compileRetrolambdaTest SKIPPED
:compileRetrolambda
:compileDesktopJava UP-TO-DATE
:processDesktopResources UP-TO-DATE
:desktopClasses UP-TO-DATE
:run
Exception in thread "Thread-6" java.lang.NullPointerException
    at com.sun.javafx.text.PrismTextLayout.layout(PrismTextLayout.java:1209)
    at com.sun.javafx.text.PrismTextLayout.ensureLayout(PrismTextLayout.java:223)
    at com.sun.javafx.text.PrismTextLayout.getBounds(PrismTextLayout.java:246)
    at javafx.scene.text.Text.getLogicalBounds(Text.java:358)
    at javafx.scene.text.Text.getYRendering(Text.java:1069)
    at javafx.scene.text.Text.access$4400(Text.java:95)
    at javafx.scene.text.Text$TextAttribute$11.computeValue(Text.java:1785)
    at javafx.scene.text.Text$TextAttribute$11.computeValue(Text.java:1777)
    at javafx.beans.binding.ObjectBinding.get(ObjectBinding.java:153)
    at javafx.beans.binding.ObjectExpression.getValue(ObjectExpression.java:50)
    at javafx.beans.property.ObjectPropertyBase.get(ObjectPropertyBase.java:132)
    at com.sun.javafx.scene.control.skin.TextFieldSkin.lambda$new$198(TextFieldSkin.java:233)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:105)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
    at javafx.beans.property.ObjectPropertyBase.access$000(ObjectPropertyBase.java:51)
    at javafx.beans.property.ObjectPropertyBase$Listener.invalidated(ObjectPropertyBase.java:233)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.binding.ObjectBinding.invalidate(ObjectBinding.java:172)
    at javafx.scene.text.Text.impl_geomChanged(Text.java:769)
    at javafx.scene.text.Text.needsTextLayout(Text.java:194)
    at javafx.scene.text.Text.needsFullTextLayout(Text.java:189)
    at javafx.scene.text.Text.access$200(Text.java:95)
    at javafx.scene.text.Text$2.invalidated(Text.java:389)
    at java.lang.ArrayIndexOutOfBoundsException
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException
Exception in thread "JavaFX Application Thread" java.lang.ArrayIndexOutOfBoundsException
Exception in thread "JavaFX Application Thread"
[sts] Cancellation request posted...
[sts] Build cancelled
[sts] Time taken: 0 min, 39 sec
[sts] -----------------------------------------------------

还有,当我点击 Eclipse 控制台中的红色方块停止程序时,需要很长时间才能响应,为什么会这样?毕竟它只是一个 TextField 和一个 Button...

screenshot

【问题讨论】:

    标签: java android loops gradle javafx


    【解决方案1】:

    您正在从后台线程访问和修改切换按钮和文本字段,这违反了 JavaFX 的单线程规则(例如,请参阅 Application documentation 中的“线程”部分)。

    您需要使用Platform.runLater() 修改FX 应用程序线程上的文本字段。您还应该使用AtomicBoolean 来标记是否停止线程。

    最后,为了让应用程序正常退出,让你的后台线程成为守护线程:

    import java.util.concurrent.atomic.AtomicBoolean;
    
    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.TextField;
    import javafx.scene.control.ToggleButton;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    public class UpdateTextFieldOnToggle extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            ToggleButton toggleButton = new ToggleButton("Generate");
            TextField textField = new TextField();
    
            AtomicBoolean running = new AtomicBoolean();
    
            toggleButton.setOnAction(event -> {
    
                running.set(toggleButton.isSelected());
    
                try {
    
                    if (running.get()) {
                        Thread t = new Thread(new Runnable() {
                            public void run() {
                                while (running.get()) {
                                    try {
                                        Thread.sleep(200);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
    
                                    Platform.runLater(() -> {
                                        double rand = Math.random();
                                        textField.setText(String.format("%f", rand));
                                    });
                                }
                            }
                        });
                        t.setDaemon(true);
                        t.start();
                    }
    
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            });
    
            VBox root = new VBox(10, toggleButton, textField);
            root.setAlignment(Pos.CENTER);
            root.setPadding(new Insets(24));
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    请注意,对于此类功能,动画 API 通常更方便:

    import javafx.animation.Animation;
    import javafx.animation.KeyFrame;
    import javafx.animation.Timeline;
    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.TextField;
    import javafx.scene.control.ToggleButton;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    import javafx.util.Duration;
    
    public class UpdateTextFieldOnToggle extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            ToggleButton toggleButton = new ToggleButton("Generate");
            TextField textField = new TextField();
    
            Timeline changeTextField = new Timeline(
                new KeyFrame(Duration.millis(200), event -> {
                    double rand = Math.random();
                    textField.setText(String.format("%f", rand));
                })  
            );
    
            changeTextField.setCycleCount(Animation.INDEFINITE);
    
            toggleButton.setOnAction(event -> {
                if (toggleButton.isSelected()) {
                    changeTextField.play();
                } else {
                    changeTextField.stop();
                }
            });
    
            VBox root = new VBox(10, toggleButton, textField);
            root.setAlignment(Pos.CENTER);
            root.setPadding(new Insets(24));
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    【讨论】:

    • 非常感谢您的详尽解释!这正是我的意思!
    猜你喜欢
    • 2016-11-07
    • 2023-03-12
    • 2020-06-06
    • 2017-01-20
    • 1970-01-01
    • 2014-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多