JavaFX的:如何绑定两个值?(JavaFX: How to bind two values?)

2019-08-18 15:43发布

我这里是新来的家伙:)

我有一个小问题,它涉及在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;
    }

}

Answer 1:

为什么你的应用程序无法正常工作

正在发生的事情是,你在它自己的线程中运行任务,在任务设置秒属性,则绑定触发标签文本的即时更新,同时还对任务线程。

这违反了规则为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())的方法?

是的,你可以使用多个任务。 每个任务可以是同一类型或不同类型的。

您可以创建一个单独的线程和线程依次运行每个任务,或者可以创建多个线程和并行运行的任务。

对于管理多个任务,您可以创建一个监工任务 。 有时也有必要使用一个服务 ,用于管理多个任务和执行者来管理多线程框架。

有一个的例子TaskServiceExecutors统筹办法: 由单一服务在每个任务创建多个并行任务 。

在每一个任务中,您可以将无runlater呼叫,单runlater来电或多个runlater电话。

因此,有可用具有很大的灵活性。

或者,也许我应该创建将从其他任务和更新UI只需要一个数据总任务?

如果是复杂值得它,你可以使用一个协调任务的方法是这样的。 还有,在这种方法的一个例子渲染300点的图表关闭屏幕,并将其保存到文件 。



Answer 2:

您的“定时器”类不符合的JavaFX豆约定:

public String getSeconds();
public void setSeconds(String seconds);
public StringProperty secondsProperty();


文章来源: JavaFX: How to bind two values?