我发布了一个与之前类似的问题,但我还不够具体。这是我的代码的简化版本:
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上升,并在用户点击"暂停"时暂停,并在用户点击"播放"时再次恢复。
问题是每当我点击" Play"按钮,程序进入无限循环,我无法按暂停按钮停止它。我这样做的方式有问题吗?是否有任何技巧让这个工作?任何帮助都将非常感激。
另外,我知道其中一些可能有语法错误,但我知道我的代码副本是正确的,我更多地考虑了如何使其工作的逻辑。 / p>
答案 0 :(得分:1)
您的实施存在两个大问题:
play
设置为false
后,循环将退出,点击简历将不执行任何操作。有几种方法可以解决这个问题。我将使用Thread
和CountDownLatch
演示一个。
我无法解释问题范围内的线程是什么(SO和各处的大量材料),但这里的相关内容是在不是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
实例化了它)。这就是Resume所做的。
在线程上调用setDaemon(true)
可确保它允许JVM退出而无需等待它。如果线程必须在JVM存在之前完成(例如,它不是后台线程),则可以将其删除。
我做了一个锁存器volatile
,它保证不同的线程会看到相同的值。另请参阅Do you ever use the volatile keyword in Java?和其他可用来源。在这个特定的情况下,你不需要点对点线程同步,所以它不会有明显的效果,但它应该在那里。
请注意,我在Start上添加了一个小的检查,表明线程尚未运行,因为在线程运行时启动线程会引发异常。如果在执行期间按下“开始”,则您没有指定要执行的操作。
虽然与您的问题无关,但我们已经演示了如何利用JavaFX binding API自动更新按钮的文字boolean
的价值。我"晋升"控件boolean
到属性并将按钮的文本绑定到其值。但这对于这种情况可能没那么有用。
备注:强>
sizeToScene
,这会使之前的呼叫变得多余。==
检查布尔值,您可以直接使用它:if (b == true)
相当于if (b)
而if (b == false)
相当于{ {1}}。if (!b)
的更好名称将代表一个州("正在运行" /"暂停")而不是一个动作("运行&# 34; /"暂停"。)