我同时使用Javafx和线程,并且遇到了这个问题,我制作了一个按钮,然后单击该按钮(使用事件处理程序)时,我做了一个for循环,将按钮更改为1,2,3,4 ,5,然后在每个中间延迟一秒钟。像倒计时!
但是发生的事情是延迟了5秒钟,并将按钮的文本更改为5。
问题是我希望看到它在1到5之间变化,但我看到的只是在5秒延迟结束时为5。我认为它会更改按钮文本,但看不到。我可能与类中的.show()方法有关Javafx。
.show()
Javafx
public class HewoWorld extends Application implements EventHandler<ActionEvent> { Thread t = new Thread(); Button butt; boolean buttWasClicked = false; Circle circ1 = new Circle(40, 40, 30, Color.RED); Circle circ2 = new Circle(100, 100, 30, Color.BLUE); Group root; Scene scene; Stage disStage = new Stage(); int i = 1; public static void main(String[] args) { launch(args); } public void start(Stage stage) throws Exception { disStage.setTitle("tests stuffs"); Screen screen = Screen.getPrimary(); Rectangle2D bounds = screen.getVisualBounds(); double windh = bounds.getHeight()/2+150;//sets height of screen double windw = bounds.getWidth()/3;//sets width of screen Pane layout = new Pane(); butt = new Button(); butt.setText("Hello world"); root = new Group(circ1, circ2, butt); scene = new Scene(root, 800, 400); disStage.setWidth(windw); disStage.setHeight(windh); butt.setLayoutX(200); butt.setLayoutY(200); butt.setOnAction(this); disStage.setScene(scene); disStage.show(); } public void handle(ActionEvent event) { if (event.getSource() == butt && buttWasClicked == false) { try { butt.setText(i+""); t.sleep(1000); i++; } catch(Exception q) { } circ1 = new Circle(40, 40, 30, Color.BLACK); circ2 = new Circle(100, 100, 30, Color.RED); } } }
为什么您的代码不起作用
您的代码不起作用的原因是您阻止了FX Application Thread。
与所有UI工具包一样(几乎?),JavaFX是单线程UI工具包。这意味着所有事件处理程序以及UI的所有呈现都在单个线程(称为FX Application Thread)上执行。
在您的代码中,您有一个事件处理程序需要花一秒钟以上的时间来运行,因为它通过调用暂停一秒钟Thread.sleep(...)。在该事件处理程序运行时,无法重绘UI(因为单个线程无法一次执行两项操作)。因此,尽管按钮文本的值立即更改,但是直到该handle(...)方法完成运行后,新值才会真正显示在屏幕上。如果forhandle方法中有一个循环,则在整个循环(以及该方法中的任何其他内容)完成之前,不会呈现任何内容。
Thread.sleep(...)
handle(...)
for
如何修复
在JavaFX中执行所需操作的最简单方法是使用a Timeline处理暂停。在Timeline为你适当地管理线程:
Timeline
import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import javafx.util.Duration; public class CountingButton extends Application { @Override public void start(Stage primaryStage) { Button button = new Button("Count"); Timeline timeline = new Timeline(); for (int count = 0; count <= 5 ; count++) { final String text = Integer.toString(count); KeyFrame frame = new KeyFrame(Duration.seconds(count), event -> button.setText(text)); timeline.getKeyFrames().add(frame); } button.setOnAction(e -> timeline.play()); primaryStage.setScene(new Scene(new StackPane(button), 120, 75)); primaryStage.show(); } public static void main(String[] args) { launch(args); } }
通常,对于更改特定时间点的用户界面外观,JavaFX Animation API(另请参见教程)可能会很有用,尤其是Timeline和PauseTransition。
PauseTransition
一种“较低级别”的方法是创建Thread自己并在该线程中暂停。这要先进得多:您需要小心地在FX Application线程上而不是在您创建的线程上更新UI。您可以通过以下方式进行此操作Platform.runLater(...):
Thread
Platform.runLater(...)
import javafx.application.Application; import javafx.application.Platform; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class CountingButton extends Application { @Override public void start(Stage primaryStage) { Button button = new Button("Start"); button.setOnAction(e -> { Thread thread = new Thread(() -> { for (int i = 0; i <= 5 ; i++) { final String text = "Count: "+i ; Platform.runLater(() -> button.setText(text)); try { Thread.sleep(1000); } catch (InterruptedException exc) { exc.printStackTrace(); } } }); thread.start(); }); primaryStage.setScene(new Scene(new StackPane(button), 120, 75)); primaryStage.show(); } public static void main(String[] args) { launch(args); } }