Java FX线程安全暂停/睡眠
我有一个JavaFX应用程序,里面有大约20个标签。当用户单击标签时,我需要标签闪烁红色。为了使标签闪烁为红色,我使用了一个为swing开发的线程,但它已转换为JavaFX。它工作了一段时间,但我最近跟踪了应用程序锁定到标签的动画。我制作动画的方式很简单:Java FX线程安全暂停/睡眠,java,multithreading,animation,javafx,Java,Multithreading,Animation,Javafx,我有一个JavaFX应用程序,里面有大约20个标签。当用户单击标签时,我需要标签闪烁红色。为了使标签闪烁为红色,我使用了一个为swing开发的线程,但它已转换为JavaFX。它工作了一段时间,但我最近跟踪了应用程序锁定到标签的动画。我制作动画的方式很简单: new Thread(new AnimateLabel(tl,idx)).start(); tl指向标签的ArrayList,idx指向标签的索引。另一个标签附加了一个单击事件,当您单击时,它将创建为标签设置动画的线程(使其闪烁) 出于某种
new Thread(new AnimateLabel(tl,idx)).start();
tl指向标签的ArrayList,idx指向标签的索引。另一个标签附加了一个单击事件,当您单击时,它将创建为标签设置动画的线程(使其闪烁)
出于某种原因,如果有很多标签被按下,这将导致应用程序锁定
我很确定这是JavaFX的线程安全问题。我有另一个线程共享JavaFX应用程序线程,如下所示:
TimerUpdater tu = new TimerUpdater(mDS);
Timeline incHandler = new Timeline(new KeyFrame(Duration.millis(130),tu));
incHandler.setCycleCount(Timeline.INDEFINITE);
incHandler.play();
TimerUpdater将不断更新标签上的文本,即使是闪烁的标签
以下是标签动画师:
private class AnimateLabel implements Runnable
{
private Label lbl;
public AnimateLabel(Label lbl, int myIndex)
{
// if inAnimation.get(myIndex) changes from myAnim's value, stop,
// another animation is taking over
this.lbl = lbl;
}
@Override
public void run() {
int r, b, g;
r=255;
b=0;
g=0;
int i = 0;
while(b <= 255 && g <= 255)
{
RGB rgb = getBackgroundStyle(lbl.getStyle());
if(rgb != null)
{
if(rgb.g < g-16) { return; }
}
lbl.setStyle("-fx-color: #000; -fx-background-color: rgb("+r+","+g+","+b+");");
try { Thread.sleep(6); }
catch (Exception e){}
b += 4;
g += 4;
++i;
}
lbl.setStyle("-fx-color: #000; -fx-background-color: fff;");
}
}
但是,Thread.sleep(6)将被忽略。在与javafx.application共享线程时,是否有方法在稍后的运行中暂停以控制动画的速度
问候,,
Dustin我认为对JavaFX事件队列的工作原理有一点误解 1) 在普通线程上运行AnimateLabel代码将导致在该线程上执行
标签#setStyle(…)
代码-这是非法的,可能会导致您的问题
2) 如第二个示例所示,完全在JavaFX事件队列上运行AnimateLabel代码意味着事件线程将被阻塞,直到动画完成。同时,应用程序不会更新,也不会处理用户事件或重新绘制。基本上,您正在循环中更改标签样式,但没有给事件队列时间来实际重新绘制标签,这就是为什么您在屏幕上看不到任何内容
半正确的方法是两者的混合。在单独的线程中运行AnimateLabel,但将对Platform中Label\setStyle(…)
的调用包装在Platform\runLater(…)
中。这样,您只需为事件线程处理相关的工作,并让它自由地在其间执行其他工作(例如更新UI)
正如我所说的,这是一种半正确的方法,因为有一个内置的工具,可以以更简单的方式完成您想要的任务。您可能希望签出转换类。它为自定义动画提供了一种简单的方法,甚至提供了一系列预构建的子类,用于为节点的最常见属性设置动画。我认为对JavaFX事件队列的工作原理有一点误解
1) 在普通线程上运行AnimateLabel代码将导致在该线程上执行标签#setStyle(…)
代码-这是非法的,可能会导致您的问题
2) 如第二个示例所示,完全在JavaFX事件队列上运行AnimateLabel代码意味着事件线程将被阻塞,直到动画完成。同时,应用程序不会更新,也不会处理用户事件或重新绘制。基本上,您正在循环中更改标签样式,但没有给事件队列时间来实际重新绘制标签,这就是为什么您在屏幕上看不到任何内容
半正确的方法是两者的混合。在单独的线程中运行AnimateLabel,但将对Platform中Label\setStyle(…)
的调用包装在Platform\runLater(…)
中。这样,您只需为事件线程处理相关的工作,并让它自由地在其间执行其他工作(例如更新UI)
正如我所说的,这是一种半正确的方法,因为有一个内置的工具,可以以更简单的方式完成您想要的任务。您可能希望签出转换类。它为自定义动画提供了一种简单的方法,甚至提供了一系列预构建的子类,用于为节点的最常见属性设置动画。Sarcan有一个很好的答案
这个答案只是提供了修改css样式的方法的示例代码(基于)。该代码可以用作您在问题中发布的代码的替代
这种方法的一个优点是JavaFX库处理所有线程问题,确保所有代码都在JavaFX线程上执行,并消除可能存在的任何线程安全问题
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CssFlash extends Application {
private Label flashOnClick(final Label label) {
label.setStyle(String.format("-fx-padding: 5px; -fx-background-radius: 5px; -fx-background-color: lightblue;"));
DoubleProperty opacity = new SimpleDoubleProperty();
opacity.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> source, Number oldValue, Number newValue) {
label.setStyle(String.format("-fx-padding: 5px; -fx-background-radius: 5px; -fx-background-color: rgba(255, 0, 0, %f)", newValue.doubleValue()));
}
});
final Timeline flashAnimation = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 1)),
new KeyFrame(Duration.millis(500), new KeyValue(opacity, 0)));
flashAnimation.setCycleCount(Animation.INDEFINITE);
flashAnimation.setAutoReverse(true);
label.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent t) {
if (Animation.Status.STOPPED.equals(flashAnimation.getStatus())) {
flashAnimation.play();
} else {
flashAnimation.stop();
label.setStyle(String.format("-fx-padding: 5px; -fx-background-radius: 5px; -fx-background-color: lightblue;"));
}
}
});
return label;
}
@Override public void start(Stage stage) throws Exception {
TilePane layout = new TilePane(5, 5);
layout.setStyle("-fx-background-color: whitesmoke; -fx-padding: 10;");
for (int i = 0; i < NUM_LABELS; i++) {
layout.getChildren().add(flashOnClick(new Label("Click to flash")));
}
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private static final int NUM_LABELS = 20;
public static void main(String[] args) { Application.launch(args); }
}
导入javafx.animation.*;
导入javafx.application.application;
导入javafx.beans.property.*;
导入javafx.beans.value.*;
导入javafx.event.EventHandler;
导入javafx.scene.scene;
导入javafx.scene.control.Label;
导入javafx.scene.input.MouseEvent;
导入javafx.scene.layout.TilePane;
导入javafx.stage.stage;
导入javafx.util.Duration;
公共类CssFlash扩展了应用程序{
专用标签flashOnClick(最终标签){
label.setStyle(String.format(“-fx padding:5px;-fx background radius:5px;-fx background color:lightblue;”);
DoubleProperty不透明度=新的SimpleDoubleProperty();
addListener(新的ChangeListener(){
@覆盖公共无效更改(observeValueSarcan有一个很好的答案
这个答案只是提供了修改css样式的方法的示例代码(基于)。该代码可以作为您在问题中发布的代码的替代
这种方法的一个优点是JavaFX库处理所有线程问题,确保所有代码都在JavaFX线程上执行,并消除可能存在的任何线程安全问题
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CssFlash extends Application {
private Label flashOnClick(final Label label) {
label.setStyle(String.format("-fx-padding: 5px; -fx-background-radius: 5px; -fx-background-color: lightblue;"));
DoubleProperty opacity = new SimpleDoubleProperty();
opacity.addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> source, Number oldValue, Number newValue) {
label.setStyle(String.format("-fx-padding: 5px; -fx-background-radius: 5px; -fx-background-color: rgba(255, 0, 0, %f)", newValue.doubleValue()));
}
});
final Timeline flashAnimation = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 1)),
new KeyFrame(Duration.millis(500), new KeyValue(opacity, 0)));
flashAnimation.setCycleCount(Animation.INDEFINITE);
flashAnimation.setAutoReverse(true);
label.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override public void handle(MouseEvent t) {
if (Animation.Status.STOPPED.equals(flashAnimation.getStatus())) {
flashAnimation.play();
} else {
flashAnimation.stop();
label.setStyle(String.format("-fx-padding: 5px; -fx-background-radius: 5px; -fx-background-color: lightblue;"));
}
}
});
return label;
}
@Override public void start(Stage stage) throws Exception {
TilePane layout = new TilePane(5, 5);
layout.setStyle("-fx-background-color: whitesmoke; -fx-padding: 10;");
for (int i = 0; i < NUM_LABELS; i++) {
layout.getChildren().add(flashOnClick(new Label("Click to flash")));
}
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private static final int NUM_LABELS = 20;
public static void main(String[] args) { Application.launch(args); }
}
导入javafx.animation.*;
导入javafx.application.application;
导入javafx.beans.property.*;
导入javafx.beans.value.*;
导入javafx.event.EventHandler;
导入javafx.scene.scene;
导入javafx.scene.control.Label;
导入javafx.scene.input.MouseEvent;
导入javafx.scene.layout.TilePane;
导入javafx.stage.stage;
导入javafx.util.Duration;
公共类CssFlash扩展了应用程序{