gpt4 book ai didi

单击按钮后 JavaFX 出现无限循环错误

转载 作者:行者123 更新时间:2023-12-02 12:02:45 25 4
gpt4 key购买 nike

我之前发布了一个与此类似的问题,但我不够具体。这是我的代码的简化版本:

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.scene.control.ProgressBar;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.layout.HBox;
import javafx.scene.Scene;
import javafx.concurrent.Task;
import javafx.stage.Stage;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.control.Button;
import javafx.scene.control.MenuBar;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Separator;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.geometry.Pos;
import java.net.URL;
import javafx.scene.image.Image;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Slider;

public class ProgressTest extends Application {

boolean play = true;
int x = 0;
@Override
public void start(Stage stage) {
GridPane pane = new GridPane(); //pane and Hboxes
HBox hbox = new HBox();

Button update = new Button("Start");
update.setOnAction( e -> {
while(play == true)
{
System.out.println(++x);
}
});

Button pause = new Button("Pause");
pause.setOnAction( e -> {
if(play == true)
{
pause.setText("Play");
play = false;
}

else
{
pause.setText("Pause");
play = true;
}
});


hbox.getChildren().addAll(update, pause);
pane.add(hbox, 0, 1);
Scene scene = new Scene(pane);
stage.setMaxWidth(655);
stage.setMaxHeight(620);

stage.setTitle("Gallery!");
stage.setScene(scene);

stage.sizeToScene();

stage.show();

}


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

}

这段代码的想法是,当用户单击“开始”按钮时,程序应该打印出 x up,并在用户点击“暂停”时暂停,并在用户点击“播放”时再次恢复.

问题是,每当我单击“播放”按钮时,程序就会进入无限循环,并且我无法按暂停按钮来停止它。我处理这件事的方式有问题吗?有什么技巧可以让它发挥作用吗?任何帮助将不胜感激。

此外,我知道其中一些可能存在语法错误,但我知道它在我的代码副本上是正确的,我更多地考虑了如何使其工作背后的逻辑。

最佳答案

您的实现有两个大问题:

  1. GUI 在 JavaFX 线程上运行。为了保持响应,其中的任何操作都必须快速完成。当您在此线程上运行像循环这样的长计算时,整个 GUI 都会被阻止。
  2. 在您设法将 play 设置为 false 后,循环将退出,并且单击恢复将不会执行任何操作。

有几种方法可以解决这个问题。我将使用 Thread 来演示一个和 CountDownLatch .

我无法解释问题范围内的线程是什么(关于 SO 和到处的大量 Material ),但这里相关的是,在不是 JavaFX 线程的线程上执行昂贵的操作将解决第 1 点。

CountDownLatch 用于阻止一个线程(或多个线程)的执行,直到锁存器被释放/破坏,此时线程将继续执行。它用一个 int 进行初始化,表示在释放之前需要倒计时的次数。到达闩锁的 await 方法的线程会阻塞,直到闩锁的 countDown 方法被调用指定的次数。

这里是一个示例代码:

import java.util.concurrent.CountDownLatch;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class ProgressTest extends Application {

volatile CountDownLatch cl = new CountDownLatch(1);

@Override
public void start(Stage stage) {
Thread thread = new Thread(() -> {
int x = 0;
while (true) {
try {
cl.await();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println(++x);
}
});
thread.setDaemon(true);

Button update = new Button("Start");
update.setOnAction(e -> {
if (!thread.isAlive()) {
cl.countDown();
thread.start();
}
});

BooleanProperty running = new SimpleBooleanProperty(true);

Button pause = new Button("Pause");
pause.textProperty().bind(Bindings.when(running).then("Pause").otherwise("Play"));
pause.setOnAction(e -> {
if (running.get()) {
cl = new CountDownLatch(1);
}
else {
cl.countDown();
}
running.set(!running.get());
});

HBox hbox = new HBox(update, pause);
Scene scene = new Scene(hbox);
stage.setScene(scene);
stage.setTitle("Gallery!");
stage.sizeToScene();
stage.show();
}

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

线程像您一样使用 while(true) 循环“尽可能快地”吐出数字,只是可以通过到达 await 方法来暂停。当您按“开始”时,锁存器将被破坏,线程将启动并连续执行。当您按 Pause 时,会在其位置创建一个新的闩锁(闩锁是一次性的,无法重置),这会导致线程在 await 中等待,直到有人用countDown 方法(一次调用就足够了,因为我们用 1 实例化了它)。这就是按“恢复”的作用。

在线程上调用 setDaemon(true) 可确保 JVM 无需等待即可退出。如果线程必须在 JVM 存在之前完成(例如,它不是后台线程),则可以将其删除。

我将锁存器设置为 volatile ,这保证了不同的线程将看到相同的值。另请参阅Do you ever use the volatile keyword in Java?以及其他可用的来源。在这种特定情况下,您不需要点线程同步,因此它不会产生明显的效果,但它仍然应该存在。

请注意,我在 Start 上添加了一个小检查,以确保线程尚未运行,因为在线程运行时启动线程会引发异常。您没有指定如果在执行过程中按下“开始”该怎么办。

<小时/>

虽然与您的问题无关,但我演示了如何利用 JavaFX binding使用 boolean 值自动更新按钮文本的 API。我将控件“boolean”“提升”为属性,并将按钮的文本绑定(bind)到其值。但对于这种情况来说它可能没那么有用。

注释:

  • 您设置场景的高度和宽度,然后调用 sizeToScene,这使得之前的调用变得多余。
  • 不需要使用 == 检查 boolean 值,可以直接使用它:if (b == true) 相当于 if (b)if (b == false) 相当于 if (!b)
  • boolean 值的更好名称应该是代表一种状态(“运行”/“暂停”)而不是一种操作(“运行”/“暂停”)。

关于单击按钮后 JavaFX 出现无限循环错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47125540/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com