Wic*_*cia 4 binding multithreading bind task javafx-2
我是这里的新人:)
我有一个小问题,涉及JavaFX中的绑定.我创建了一个作为时钟工作的Task,并返回必须在特殊标签(label_Time)中设置的值.此标签显示了测验中玩家回答的剩余时间.
问题是如何使用计时器任务自动更改标签中的值?我尝试以这种方式将来自计时器任务(秒)的值链接到label_Time值...
label_Time.textProperty().bind(timer.getSeconds());
Run Code Online (Sandbox Code Playgroud)
......但它不起作用.做这件事有什么办法吗?
在此先感谢您的回答!:)
Controller类中的Initialize方法:
public void initialize(URL url, ResourceBundle rb) {
Timer2 timer = new Timer2();
label_Time.textProperty().bind(timer.getSeconds());
new Thread(timer).start();
}
Run Code Online (Sandbox Code Playgroud)
任务类"Timer2":
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;
}
}
Run Code Online (Sandbox Code Playgroud)
jew*_*sea 11
为什么你的应用程序不起作用
发生的事情是你在它自己的线程上运行任务,在任务中设置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());
}
});
Run Code Online (Sandbox Code Playgroud)
这确保了当您写出属性时,您已经在JavaFX应用程序线程上,这样当后续更改触发绑定标签文本时,该更改也将发生在JavaFX应用程序线程上.
关于财产命名惯例
确实,该程序与Matthew指出的JavaFX bean约定不符.符合这些约定既有助于使程序更容易理解,也有助于使用PropertyValueFactory之类的东西,它反映了属性方法名称,以允许表和列表单元格在更新基础属性时自动更新它们的值.但是,对于您的示例,不遵循JavaFX bean约定并不能解释为什么程序不起作用.
替代解决方案
以下是使用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();
}
}
Run Code Online (Sandbox Code Playgroud)
有关任务执行策略的其他问题的更新
是否可以运行多个包含
Platform.runLater(new Runnable())方法的任务?
是的,您可以使用多个任务.每个任务可以是相同类型或不同类型.
您可以创建单个线程并按顺序在线程上运行每个任务,也可以创建多个线程并并行运行任务.
要管理多个任务,您可以创建监督任务.有时使用Service来管理多个任务和Executors框架来管理多个线程是合适的.
有一个的例子Task,Service,Executors统筹的办法:由单一服务在每个任务创建多个并行任务.
在每项任务中,您不能runlater拨打任何电话,一个runlater电话或多个runlater电话.
因此,有很多灵活性.
或者我应该创建一个通用任务,只从其他任务中获取数据并更新UI?
是的,如果复杂性需要,你可以使用这样的协调任务方法.屏幕上的Render 300图表中有一个这样的方法示例,并将它们保存到文件中.