首页 > 解决方案 > ScalaFX:是否可以在应用程序对象以外的对象中定义控件?

问题描述

我想要完成的是:拥有一个ScalaFX应用程序,其中包含一些很好的有序s ,object称为Buttons,等等,以保持一切正常有序。LabelsCheckboxes

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

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

这段代码显示了一个带有标签和两个按钮的窗口,这些按钮更改了标签的文本。

这很好用。

但是当我的代码随着更多控件的增加而增长时,事情就会变得一团糟。

这就是为什么我试图将控件转移到其他objects(在不同的文件中)。我已将标签放入一个名为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

有谁知道我正在尝试做的事情是否可能?

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

我在这里忽略了一些明显的东西吗?

任何帮助将非常感激。

标签: controlsscalafx

解决方案


首先,您的方法完全可以。

您看到的错误实际上与Java无关——它是Scala编译器的输出!它只是说,当它期待另一种类型的元素(在这种情况下是一个实例)时,它已经提供了一种类型的元素(在这种情况下,一个接受 ascalafx.event.ActionEvent并返回的函数)。Unitjavafx.event.EventHandler[javafx.event.ActionEvent]

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

解决方案是确保将以下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元素时,您需要格外小心。Anobject在第一次被引用时被初始化。如果它首先被未在JavaFX 应用程序线程上执行的代码引用,那么您将获得线程错误异常。一种可能的选择是将此类代码放入deforlazy val成员中,以便仅在直接引用时执行它们。

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

有关JavaFX 应用程序线程的更多信息,请参阅JavaFX文档


推荐阅读