斯卡拉外汇。活绑定

ScalaFX. Live binding

我有一个简单的 UI window,只有一个文本节点, 显示当前时间并绑定到模型:

import model.Clock

import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.beans.property.StringProperty
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.layout.HBox
import scalafx.scene.text.Text
import scalafx.Includes._

object Main extends JFXApp {
    val clock = new Text()
    clock.textProperty().bind( new StringProperty(Clock.curTime) )

    stage = new PrimaryStage {
        onShowing = handle { Clock.startClock }
        title = "ScalaFX clock"
        scene = new Scene {
            content = new HBox {
                padding = Insets(50, 80, 50, 80)
                children = clock
            }
        }
    }
}

还有一个模型:

import java.util.Date
import java.text.SimpleDateFormat

object Clock {
    var curTime = ""
    private lazy val dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss")

    def startClock = {
        /*A code, witch runs in another Thread and 
          changes curTime every 1 second skipped here.
          Only one change of curTime for simplicity.
        */

        curTime = dateFormat format new Date
    }
}

我的问题是:当 curTime 变量改变时,UI 中的文本节点没有改变。

我猜它在 stratup 上渲染过一次。如何让文本节点每次都显示 curTime 的新值,curTime 改变了?

对不起我的英语:(

问题是 Clock.curTime 只是一个变量,其值会定期更新 - 它不是观察到的 属性,所以当它被更改时什么也不会发生。特别是,该变量与 Text 元素的内容

之间没有 link

您所做的是用该值初始化 StringProperty - 但由于 属性 本身从未更新,标签也不会更新。我想你想做的是让 curTime 成为 StringProperty 而不仅仅是 String。现在,每当您更改 属性 的值时,Text 元素的值将相应更改。

您应该注意,与 JavaFX/ScalaFX 的交互只能发生在 JavaFX 应用程序线程,所以如果您尝试从另一个线程更新 curTime,您将 运行 遇到问题。实现这一目标的一种方法是将实际更新 curTime 的代码传递给 Platform.runLater.

但是,更简单的方法是使用定时 ScalaFX 事件从 JavaFX/[ 中定期更新 curTime =34=]ScalaFX,如下:

import model.Clock
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.layout.HBox
import scalafx.scene.text.Text
import scalafx.Includes._

object Main
extends JFXApp {

  val clock = new Text()

  // Clock.curTime is now a StringProperty, which we bind to clock's text.  Since clock is
  // created lazily, it starts the timer event, so we do not need startClock either.
  clock.text <== Clock.curTime

  // Create the stage & scene.
  stage = new PrimaryStage {
    title = "ScalaFX clock"
    scene = new Scene {
      content = new HBox {
        padding = Insets(50, 80, 50, 80)
        children = clock
      }
    }
  }
}

Clock.scala中:

import java.util.Date
import java.text.SimpleDateFormat
import scalafx.animation.PauseTransition
import scalafx.application.Platform
import scalafx.beans.property.StringProperty
import scalafx.util.Duration
import scalafx.Includes._

// Object should be constructed lazily on the JFX App Thread. Verify that...
object Clock {
  assert(Platform.isFxApplicationThread)

  private val dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss")

  val curTime = new StringProperty(getDate())

  // Update the curTime property every second, using a timer.
  // Note: 1,000 ms = 1 sec
  val timer = new PauseTransition(Duration(1000))
  timer.onFinished = {_ =>
    curTime.value = getDate()
    timer.playFromStart() // Wait another second, or you can opt to finish instead.
  }

  // Start the timer.
  timer.play()

  private def getDate() = dateFormat.format(new Date())
}