ScalaFX:是否可以在应用程序对象以外的对象中定义控件?

ScalaFX: Is it possible to define controls in objects other than the application object?

我想要完成的是:拥有一个 ScalaFX 应用程序,其中包含一些名为 ButtonsLabels 的有序 objectCheckboxes 等等,以保持一切井井有条。

这里有一个小例子来说明我的意思:

package ButtonsAndLabel

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.control.{ Button, Label }
import scalafx.event.ActionEvent

object Main extends JFXApp {

  stage = new JFXApp.PrimaryStage {
    title = "Test-Program"

    scene = new Scene(300, 200) {
      val label = new Label("Nothing happened yet") {
        layoutX = 20
        layoutY = 20
      }
      val button1 = new Button("Button 1") {
        layoutX = 20
        layoutY = 50
        onAction = (e: ActionEvent) => {
          label.text = "B1 klicked"
        }
      }
      val button2 = new Button("Button 2") {
        layoutX = 20
        layoutY = 80
        onAction = (e: ActionEvent) => {
          label.text = "B2 klicked"
        }
      }

      content = List(label, button1, button2)
    }
  }
}

此代码显示了一个带有标签和两个按钮的 window,按钮更改标签的文本。

效果很好。

但是当我的代码随着更多的控件而增长时,事情就变得混乱了。

这就是我尝试将控件转移到其他 object 的原因(在不同的文件中)。我已将标签放入一个名为 Labels:

的对象中
package ButtonsAndLabel

import scalafx.scene.control.Label
import scalafx.event.ActionEvent

object Labels {
  val label = new Label("Nothing happened yet") {
    layoutX = 20
    layoutY = 20
  }
}

当我使用

将其导入主文件时
import Labels.label

一切正常。

但后来我尝试将按钮放入 Buttons 对象中:

package ButtonsAndLabel

import scalafx.scene.control.Button
import scalafx.event.ActionEvent
import Labels.label

object Buttons {
  val button1 = new Button("Button 1") {
    layoutX = 20
    layoutY = 50
    onAction = (e: ActionEvent) => {
      label.text = "B1 klicked"
    }
  }
  val button2 = new Button("Button 2") {
    layoutX = 20
    layoutY = 80
    onAction = (e: ActionEvent) => {
      label.text = "B2 klicked"
    }
  }
}

这会在我尝试编译时带来错误消息:

[error]  found   : scalafx.event.ActionEvent => Unit
[error]  required: javafx.event.EventHandler[javafx.event.ActionEvent]
[error]     onAction = (e: ActionEvent) => {

现在我卡住了,因为我什么都不知道 Java.

有人知道我正在尝试做的事情是否可行吗?

到目前为止,我还没有在网上找到任何相关信息。这个问题并没有阻止我编写我想要的程序,但我编写的最后一个应用程序是一个文件中所有控件的真正混乱。

我是不是忽略了一些明显的东西?

任何帮助将不胜感激。

首先,你的方法完全没问题。

您看到的错误实际上与 Java 无关——它是由 Scala 编译器输出的!它只是说,当它期待另一种类型的元素(一个 javafx.event.EventHandler[javafx.event.ActionEvent] 实例,在这种情况下)。

ScalaFX 只是一组 Scala 友好的包装器 JavaFX图书馆;如果没有在两组元素之间进行转换的 implicit 转换函数,Scala 编译器会在需要时抱怨找不到 ScalaFX 元素JavaFX 元素,反之亦然.

解决方案是确保将以下 import 添加到每个 ScalaFX 源文件中:

import scalafx.Includes._

(您在主源文件的顶部有这个,但其他的没有。)

这将确保您的 ScalaFX ActionEvent 处理程序转换为 JavaFX 等价物,从而让您的生活更轻松。

这是 ScalaFX 的一种非常常见的错误类型,几乎总是通过指定上述 import 来修复。 (如果 import 不能解决您的问题,那么您通常会遇到类型混淆的真正情况,在这种情况下您只是简单地使用了错误的对象类型。)

所以,我认为您的代码应该如下所示:

Main.scala:

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.scene.Scene
import buttonsandlabel._

object Main extends JFXApp {

  stage = new JFXApp.PrimaryStage {
    title = "Test-Program"

    scene = new Scene(300, 200) {
      content = List(Labels.label, Buttons.button1, Buttons.button2)
    }
  }
}

buttonsandlabel/Labels.scala:

package buttonsandlabel

import scalafx.Includes._
import scalafx.scene.control.Label

object Labels {
  val label = new Label("Nothing happened yet") {
    layoutX = 20
    layoutY = 20
  }
}

buttonsandlabel/Buttons.scala:

package buttonsandlabel

import scalafx.Includes._
import scalafx.scene.control.Button
import scalafx.event.ActionEvent
import Labels.label

object Buttons {
  val button1 = new Button("Button 1") {
    layoutX = 20
    layoutY = 50
    onAction = (e: ActionEvent) => {
      label.text = "B1 klicked"
    }
  }
  val button2 = new Button("Button 2") {
    layoutX = 20
    layoutY = 80
    onAction = (e: ActionEvent) => {
      label.text = "B2 klicked"
    }
  }
}

(请注意,按照惯例,包名称通常全部小写。)

您需要注意的一件事是 JavaFX 应用程序线程:与 ScalaFX 交互的所有代码(或JavaFX)必须在此线程上执行。如果您从不同的线程访问 ScalaFX/JavaFX,您将收到错误异常。 (这确保所有此类应用程序都是 线程安全的。)如果您不熟悉多线程,请不要担心,ScalaFX 初始化你的应用程序以这样一种方式,这是相当微不足道的。通常,所需要的只是将初始化代码放入主应用程序对象的构造函数(扩展 JFXApp 的对象)。

当您开始在其他 类 和对象中创建 ScalaFX 元素时,您需要格外小心。 object 在第一次被引用时被初始化。如果它首先被 notJavaFX Application Thread 上执行的代码引用,那么您将得到线程错误异常.一种可能的选择是将此类代码放入 deflazy val 成员中,以便它们仅在直接引用时执行。

或者,您可能必须通过 scalafx.application.Platform.runLater() 调用您的代码。

有关 JavaFX 应用程序线程 的详细信息,请参阅 JavaFX documentation