JavaFX的:如何绑定两个值? [英] JavaFX: How to bind two values?

查看:523
本文介绍了JavaFX的:如何绑定两个值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我这里是新来的家伙:)

我有一个小问题,它涉及在JavaFX绑定。我已创建的任务是工作作为具有时钟和返回值在一个特殊的标签设置( label_Time )。该标签presents多少秒的时间玩家竞猜的答案。

的问题是如何使用定时器任务自动改变在标签值?我试图从计时器任务链接值(),以这样的方式label_Time值...

  label_Time.textProperty()绑定(timer.getSeconds());

...但它不工作。它是没有办法做这件事情?

预先感谢你的答案:!)


在控制器类初始化方法:

 公共无效的initialize(URL网址,资源包RB){        定时器2定时器=新定时器2();
        。label_Time.textProperty()绑定(timer.getSeconds());
        新的线程(定时器)。开始();
}

Task类定时器2:

 公共类定时器2扩展任务{    私有静态最终诠释SLEEP_TIME = 1000;
    私有静态诠释秒;
    私人StringProperty秒;
    公共定时器2(){
        Timer2.sec = 180;
        this.seconds =新SimpleStringProperty(180);
    }    @覆盖保护StringProperty()调用抛出异常{
        诠释迭代;        为(迭代= 0;迭代&下; 1000;迭代++){
            如果(isCancelled()){
                updateMessage(取消);
                打破;
            }            的System.out.println(狄!+秒);
            seconds.setValue(将String.valueOf(秒));
            的System.out.println(TAK!+ seconds.getValue());            //从柜台我们减去1秒
            sec--;            //阻塞线程很短的时间,但可以肯定的
            //检查取消的InterruptedException的
            尝试{
                视频下载(10);
            }赶上(InterruptedException的中断){
                如果(isCancelled()){
                    updateMessage(取消);
                    打破;
                }
            }
        }
        返回秒;
    }    公共StringProperty getSeconds(){
        返回this.seconds;
    }}


解决方案

为什么您的应用程序不能正常工作

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

这违反了JavaFX的线程处理的规则


  

应用程序必须连接节点场景,并修改那些已经连接到一个场景节点,在JavaFX应用程序线程。


这是你最初发布的程序不能正常工作的原因。


如何修复它

要修改原来的计划,使其工作,换物业的修改中的任务的 Platform.runLater 结构:

  Platform.runLater(新的Runnable(){
    @覆盖公共无效的run(){
      的System.out.println(狄!+秒);
      seconds.setValue(将String.valueOf(秒));
      的System.out.println(TAK!+ seconds.getValue());
    }
  });

这可以确保当你写出来的财产,你已经是JavaFX应用程序线程上,这样,当绑定的标签文字的后续更改火灾,也将在JavaFX应用程序线程上发生的变化。


在属性命名约定

这是事实,程序不对应的JavaFX豆约定马修指出。符合这些公约是使程序更加容易理解,也为利用的东西,如<一个有用的都href=\"http://docs.oracle.com/javafx/2/api/javafx/scene/control/cell/PropertyValueFactory.html\">PropertyValueFactory反映物业的方法名称,以允许作为底层属性更新表,表单元格自动更新它们的值。但是,对于你的榜样,不遵守JavaFX的豆公约并没有解释为什么该程序无法正常工作。


替代的解决方案

下面是你的倒计时结合问题的一个替代的解决方案,它使用了JavaFX 动画框架而不是并发框架。我preFER这一点,因为它使JavaFX应用程序线程上的一切,你不必担心这是很难理解和并发的问题进行调试。

 进口javafx.animation *。
进口javafx.application.Application;
进口javafx.beans *。
进口javafx.beans.binding.Bindings;
导入javafx.beans.property *。
导入javafx.event *。
进口javafx.geometry.Pos;
导入javafx.scene *。
导入javafx.scene.control *。
进口javafx.scene.layout.VBox;
进口javafx.stage.Stage;
进口javafx.util.Duration;公共类CountdownTimer扩展应用{
  @覆盖公共无效启动(最后阶段阶段)抛出异常{
    最后的倒计时读秒=新的倒计时(10);
    最后CountDownLabel countdownLabel =新CountDownLabel(倒计时);    最终按钮countdownButton =新按钮(开始);
    countdownButton.setOnAction(新的EventHandler&LT;&ActionEvent的GT;(){
      @覆盖公共无效手柄(ActionEvent的T){
        countdownButton.setText(重新启动);
        countdown.start();
      }
    });    垂直框布局=新的垂直框(10);
    。layout.getChildren()的addAll(countdownLabel,countdownButton);
    layout.setAlignment(Pos.BASELINE_RIGHT);
    layout.setStyle( - FX-背景色:玉米穗黄; -fx-填充:20; -fx-字体大小:20;);    stage.setScene(新场景(布局));
    stage.show();
  }  公共静态无效的主要(字串[] args)抛出异常{
    启动(参数);
  }
}类CountDownLabel扩展标记{
  公共CountDownLabel(最后的倒计时读秒){
    。textProperty()绑定(Bindings.format(%3D,countdown.timeLeftProperty()));
  }
}类倒计时{
  私人最终ReadOnlyIntegerWrapper的timeleft;
  私人最终ReadOnlyDoubleWrapper timeLeftDouble;
  私人最终时间表时间表;  公共ReadOnlyIntegerProperty timeLeftProperty(){
    返回timeLeft.getReadOnlyProperty();
  }  公共COUNTDOWN(最终诠释时间){
    的timeleft =新ReadOnlyIntegerWrapper(时间);
    timeLeftDouble =新ReadOnlyDoubleWrapper(时间);    时间表=新的时间线(
      新的关键帧(
        Duration.ZERO,
        新的键值(timeLeftDouble,时间)
      )
      新的关键帧(
        Duration.seconds(时间),
        新的键值(timeLeftDouble,0)
      )
    );    timeLeftDouble.addListener(新InvalidationListener(){
      @覆盖公共无效无效(可观察O){
        timeLeft.set((int)的Math.ceil(timeLeftDouble.get()));
      }
    });
  }  公共无效的start(){
    timeline.playFromStart();
  }
}


更新任务执行战略的其他问题


  

是否有可能运行多个任务,其中包括一个 Platform.runLater(新的Runnable())方法?


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

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

有关管理多个任务,您可以创建一个监工任务。有时是合适的使用服务的用于管理多个任务和< A HREF =htt​​p://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html>执行人用于管理多线程框架。

有一个工作服务执行人的例子统筹方法:由单一服务于每个任务创建多个并行任务。

在每一个任务,你可以将任何 runlater 电话,一个 runlater 来电或多个 runlater 通话。

所以有可用的很大的灵活性。


  

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


是的,你可以使用一个协调任务的方法是这样的复杂性,如果它值得。有href=\"https://gist.github.com/jewelsea/5072743\">渲染300的图表关闭屏幕和保存他们文件在

I'm new guy here :)

I have a small problem which concerns binding in JavaFX. I have created Task which is working as a clock and returns value which has to be set in a special label (label_Time). This label presents how many seconds left for player's answer in quiz.

The problem is how to automatically change value in label using the timer task? I tried to link value from timer Task (seconds) to label_Time value in such a way...

label_Time.textProperty().bind(timer.getSeconds());

...but it doesn't work. Is it any way to do this thing?

Thanks in advance for your answer! :)


Initialize method in Controller class:

public void initialize(URL url, ResourceBundle rb) {

        Timer2 timer = new Timer2();
        label_Time.textProperty().bind(timer.getSeconds());
        new Thread(timer).start();  
}

Task class "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;
    }

}

解决方案

Why your app does not work

What is happening is that you run the task on it's own thread, set the seconds property in the task, then the binding triggers an immediate update of the label text while still on the task thread.

This violates a rule for JavaFX thread processing:

An application must attach nodes to a Scene, and modify nodes that are already attached to a Scene, on the JavaFX Application Thread.

This is the reason that your originally posted program does not work.


How to fix it

To modify your original program so that it will work, wrap the modification of the property in the task inside a Platform.runLater construct:

  Platform.runLater(new Runnable() {
    @Override public void run() {
      System.out.println("TIK! " + sec);
      seconds.setValue(String.valueOf(sec));
      System.out.println("TAK! " + seconds.getValue());
    }
  });

This ensures that when you write out to the property, you are already on the JavaFX application thread, so that when the subsequent change fires for the bound label text, that change will also occur on the JavaFX application thread.


On Property Naming Conventions

It is true that the program does not correspond to JavaFX bean conventions as Matthew points out. Conforming to those conventions is both useful in making the program more readily understandable and also for making use of things like the PropertyValueFactory which reflect on property method names to allow table and list cells to automatically update their values as the underlying property is updated. However, for your example, not following JavaFX bean conventions does not explain why the program does not work.


Alternate Solution

Here is an alternate solution to your countdown binding problem which uses the JavaFX animation framework rather than the concurrency framework. I prefer this because it keeps everything on the JavaFX application thread and you don't need to worry about concurrency issues which are difficult to understand and debug.

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();
  }
}


Update for additional questions on Task execution strategy

Is it possible to run more than one Task which includes a Platform.runLater(new Runnable()) method ?

Yes, you can use multiple tasks. Each task can be of the same type or a different type.

You can create a single thread and run each task on the thread sequentially, or you can create multiple threads and run the tasks in parallel.

For managing multiple tasks, you can create an overseer Task. Sometimes it is appropriate to use a Service for managing the multiple tasks and the Executors framework for managing multiple threads.

There is an example of a Task, Service, Executors co-ordination approach: Creating multiple parallel tasks by a single service In each task.

In each task you can place no runlater call, a single runlater call or multiple runlater calls.

So there is a great deal of flexibility available.

Or maybe I should create one general task which will be only take data from other Tasks and updating a UI?

Yes you can use a co-ordinating task approach like this if complexity warrants it. There is an example of such an approach in in Render 300 charts off screen and save them to files.

这篇关于JavaFX的:如何绑定两个值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆