我这里是新来的家伙:)
我有一个小问题,它涉及在JavaFX绑定。 我已创建的任务,其工作作为在一个特殊的标签(label_Time)被设定时钟,并返回值。 此标签提出多少秒留给玩家的提问回答。
问题是如何使用定时器任务会自动改变标签的价值? 我想以这样的方式来链接从计时器任务( 秒 )值label_Time值...
label_Time.textProperty().bind(timer.getSeconds());
...但它不工作。 它是没有办法做这件事情?
在此先感谢您的回答! :)
在控制器类初始化方法:
public void initialize(URL url, ResourceBundle rb) {
Timer2 timer = new Timer2();
label_Time.textProperty().bind(timer.getSeconds());
new Thread(timer).start();
}
Task类“定时器”:
public class Timer2 extends Task{
private static final int SLEEP_TIME = 1000;
private static int sec;
private StringProperty seconds;
public Timer2(){
Timer2.sec = 180;
this.seconds = new SimpleStringProperty("180");
}
@Override protected StringProperty call() throws Exception {
int iterations;
for (iterations = 0; iterations < 1000; iterations++) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
System.out.println("TIK! " + sec);
seconds.setValue(String.valueOf(sec));
System.out.println("TAK! " + seconds.getValue());
// From the counter we subtract one second
sec--;
//Block the thread for a short time, but be sure
//to check the InterruptedException for cancellation
try {
Thread.sleep(10);
} catch (InterruptedException interrupted) {
if (isCancelled()) {
updateMessage("Cancelled");
break;
}
}
}
return seconds;
}
public StringProperty getSeconds(){
return this.seconds;
}
}
为什么你的应用程序无法正常工作
正在发生的事情是,你在它自己的线程中运行任务,在任务设置秒属性,则绑定触发标签文本的即时更新,同时还对任务线程。
这违反了规则为JavaFX线程处理:
应用程序必须将节点连接场景,并修改那些已经连接到一个场景,在JavaFX应用程序线程节点。
这是你最初发布程序不工作的原因。
如何修复
要修改原来的计划,使其工作,包在里面的任务属性的修改Platform.runLater结构:
Platform.runLater(new Runnable() {
@Override public void run() {
System.out.println("TIK! " + sec);
seconds.setValue(String.valueOf(sec));
System.out.println("TAK! " + seconds.getValue());
}
});
这保证了当你写出来的财产,你已经JavaFX应用程序线程上,这样,当后续更改火的绑定的标签文本,也将JavaFX应用程序线程上发生的变化。
专属命名约定
这是事实,程序不对应的JavaFX豆约定马修指出。 符合这些公约是使程序更容易理解,也为利用喜欢的东西都是有用的PropertyValueFactory反映在财产法的名称,以便作为基础属性更新的表格和列表细胞自动更新它们的值。 然而,你的榜样,而不是JavaFX的以下豆约定并没有解释为什么该程序无法正常工作。
另一种解决方案
这里是您的倒计时结合问题的一个替代解决方案,它使用了JavaFX 动画框架而不是并发框架 。 我喜欢这个,因为它使JavaFX应用程序线程上的一切,你不必担心这是很难理解和调试并发问题。
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.*;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.event.*;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CountdownTimer extends Application {
@Override public void start(final Stage stage) throws Exception {
final CountDown countdown = new CountDown(10);
final CountDownLabel countdownLabel = new CountDownLabel(countdown);
final Button countdownButton = new Button(" Start ");
countdownButton.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent t) {
countdownButton.setText("Restart");
countdown.start();
}
});
VBox layout = new VBox(10);
layout.getChildren().addAll(countdownLabel, countdownButton);
layout.setAlignment(Pos.BASELINE_RIGHT);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20; -fx-font-size: 20;");
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
class CountDownLabel extends Label {
public CountDownLabel(final CountDown countdown) {
textProperty().bind(Bindings.format("%3d", countdown.timeLeftProperty()));
}
}
class CountDown {
private final ReadOnlyIntegerWrapper timeLeft;
private final ReadOnlyDoubleWrapper timeLeftDouble;
private final Timeline timeline;
public ReadOnlyIntegerProperty timeLeftProperty() {
return timeLeft.getReadOnlyProperty();
}
public CountDown(final int time) {
timeLeft = new ReadOnlyIntegerWrapper(time);
timeLeftDouble = new ReadOnlyDoubleWrapper(time);
timeline = new Timeline(
new KeyFrame(
Duration.ZERO,
new KeyValue(timeLeftDouble, time)
),
new KeyFrame(
Duration.seconds(time),
new KeyValue(timeLeftDouble, 0)
)
);
timeLeftDouble.addListener(new InvalidationListener() {
@Override public void invalidated(Observable o) {
timeLeft.set((int) Math.ceil(timeLeftDouble.get()));
}
});
}
public void start() {
timeline.playFromStart();
}
}
更新任务执行战略的其他问题
是否有可能运行多个任务,其中包含一个Platform.runLater(new Runnable())
的方法?
是的,你可以使用多个任务。 每个任务可以是同一类型或不同类型的。
您可以创建一个单独的线程和线程依次运行每个任务,或者可以创建多个线程和并行运行的任务。
对于管理多个任务,您可以创建一个监工任务 。 有时也有必要使用一个服务 ,用于管理多个任务和执行者来管理多线程框架。
有一个的例子Task
, Service
, Executors
统筹办法: 由单一服务在每个任务创建多个并行任务 。
在每一个任务中,您可以将无runlater
呼叫,单runlater
来电或多个runlater
电话。
因此,有可用具有很大的灵活性。
或者,也许我应该创建将从其他任务和更新UI只需要一个数据总任务?
如果是复杂值得它,你可以使用一个协调任务的方法是这样的。 还有,在这种方法的一个例子渲染300点的图表关闭屏幕,并将其保存到文件 。
您的“定时器”类不符合的JavaFX豆约定:
public String getSeconds();
public void setSeconds(String seconds);
public StringProperty secondsProperty();