scalaFX - Titledpane:如何获取内容的高度?

scalaFX - Titledpane: how do I get the heigth of the content?

我在 scalafx 中创建了一个 TiteldPane

    val titled: TitledPane = new TitledPane()

并为我的 GUI 在其中放置一些节点。 稍后我想读出标题内容的高度。
在 javaFX 中,这将通过以下方式完成:

((Region) titled.getContent()).getHeight()

但是如果我尝试读取 scala 中内容的高度:

titled.content.height

高度被标记为已弃用且无法编译。我得到了 github (scalafx/issue69) 的提示,它解释了为什么不推荐使用它,但没有解释如何替代它。

澄清一下:我想读出标题窗格内容的高度,而不仅仅是 titled.heigth。 当标题关闭时,titled.height 为 0,但我想知道当它展开时它会是什么(以检测它何时真正完成展开)。

那么,我如何在 scalafx 中执行此操作?

编辑: 这是一个显示所描述错误的示例


    import scalafx.Includes._
    import scalafx.application.JFXApp
    import scalafx.beans.property.DoubleProperty
    import scalafx.beans.value.ObservableValue
    import scalafx.collections.ObservableBuffer
    import scalafx.event.ActionEvent
    import scalafx.scene.Scene
    import scalafx.scene.control.cell.TextFieldListCell
    import scalafx.scene.control.{Button, ListView, TitledPane}
    import scalafx.scene.layout.BorderPane
    
    object TitledPaneEndOfExpansion extends JFXApp {
      val expandedHeight = new DoubleProperty()
      val data: ObservableBuffer[String] = new ObservableBuffer[String]() ++= List("some", "content", "for", "testing")
    
      stage = new JFXApp.PrimaryStage {
        title = "JavaFX: edit after rendering test"
    
        val list: ListView[String] = new ListView[String](data) {
          editable = true
          cellFactory = TextFieldListCell.forListView()
          height.onChange { (source: ObservableValue[Double, Number], oldValue: Number, newValue: Number) =>
            expandedHeight.value = titled.content.height
            println("old height is: " + oldValue.doubleValue() + "   new height is: " + newValue.doubleValue())
            if (newValue.doubleValue() == expandedHeight.value) {
              edit(1)
            }
          }
        }
    
        val titled: TitledPane = new TitledPane {
          text = "titled"
          content = list
        }
    
        scene = new Scene {
          root = new BorderPane {
            center = titled
            bottom = new Button() {
              text = "edit cell 1"
              onAction = { _: ActionEvent => list.edit(1) }
            }
          }
        }
        expandedHeight.value = titled.content.height //set to 400
        list.edit(1)
      }
    }

这里是 buid.sbt 文件:

name := "JavaFXrenderingProblem"
version := "0.1"
scalaVersion := "2.13.3"
libraryDependencies += "org.scalafx" %% "scalafx" % "15.0.1-R21"
libraryDependencies += "org.controlsfx" % "controlsfx" % "8.40.18"
// Prevent startup bug in JavaFX
fork := true
// Tell Javac and scalac to build for jvm 1.8
javacOptions ++= Seq("-source", "1.8", "-target", "1.8")
scalacOptions += "-target:jvm-1.8"
scalacOptions += "-feature"

当我用普通 sbt 编译时,我得到了编译 error-message:

[info] compiling 1 Scala source to ... JavaFXrenderingProblem\target\scala-2.13\classes ...
[error]  ... JavaFXrenderingProblem\src\main\scala\TitledPaneEndOfExpansion.scala:38:47: value height is not a member of scalafx.beans.property.ObjectProperty[javafx.scene.Node]
[error]         expandedHeight.value = titled.content.height
[error]                                               ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 3 s, completed 03.05.2021 11:09:02

当我在你的代码上执行 sbt run 时,我实际上得到了两个错误,而且我没有得到弃用错误:

[info] compiling 1 Scala source to C:\Users\SomeUser\src\SFC\target\scala-2.13\classes ...
[error] C:\Users\SomeUser\src\SFX\src\main\scala\TitledPaneEndOfExpansion.scala:23:41: value height is not a member of scalafx.beans.property.ObjectProperty[javafx.scene.Node]
[error]                 expandedHeight.value = titled.content.height
[error]                                                       ^
[error] C:\Users\MichaelAllen\src\SOSFX\src\main\scala\TitledPaneEndOfExpansion.scala:45:40: value height is not a member of scalafx.beans.property.ObjectProperty[javafx.scene.Node]
[error]         expandedHeight.value = titled.content.height //set to 400
[error]                                               ^
[error] two errors found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 3 s, completed May 3, 2021 9:58:00 AM

根据您的代码,list 值 returns TitledPane 实例 titled 的内容作为 ListView[String]。您正试图调用此对象的 height 方法。正确吗?

主要问题是 titledcontent 方法对 titled 存储的对象类型了解不够。它只知道它是从 javafx.scene.Node 派生的。这样的 Node 实例没有 height 属性,因此你的错误。 (实际上比这复杂一点,但这是解释问题的最简单方法。)

但是,您已经拥有对作为 titled 内容的对象的引用:list。因此,您可以将对 titled.content.height 的第二个引用替换为 list.heightlistheightonChanged 方法中的第一个引用可通过 source 参数访问(它标识更改值的 属性,即list.height 在这种情况下)。因此,在这种情况下,您可以将 title.content.height 替换为 source

我注意到您在示例中为 expandedHeight 使用了 DoubleProperty 类型,但您需要继续查看关联类型的 value。这不是很地道。如果你不需要这个值是反应性的,一个简单的 Double 就足够了(但这需要将 expandedHeight 声明为 var)。

合并后,生成以下代码:

import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.beans.property.DoubleProperty
import scalafx.beans.value.ObservableValue
import scalafx.collections.ObservableBuffer
import scalafx.event.ActionEvent
import scalafx.scene.Scene
import scalafx.scene.control.cell.TextFieldListCell
import scalafx.scene.control.{Button, ListView, TitledPane}
import scalafx.scene.layout.BorderPane

object TitledPaneEndOfExpansion extends JFXApp {
  var expandedHeight: Double = _
  val data: ObservableBuffer[String] = new ObservableBuffer[String]() ++= List("some", "content", "for", "testing")

  stage = new JFXApp.PrimaryStage {
    title = "JavaFX: edit after rendering test"

    val list: ListView[String] = new ListView[String](data) {
      editable = true
      cellFactory = TextFieldListCell.forListView()
      height.onChange { (source: ObservableValue[Double, Number], oldValue: Number, newValue: Number) =>
        expandedHeight = source.value
        println("old height is: " + oldValue.doubleValue() + "   new height is: " + newValue.doubleValue())
        if (newValue.doubleValue() == expandedHeight) {
          edit(1)
        }
      }
    }

    val titled: TitledPane = new TitledPane {
      text = "titled"
      content = list
    }

    scene = new Scene {
      root = new BorderPane {
        center = titled
        bottom = new Button() {
          text = "edit cell 1"
          onAction = { _: ActionEvent => list.edit(1) }
        }
      }
    }
    expandedHeight = list.height.value //set to 400
    list.edit(1)
  }
}

然后您的代码将编译并运行。

已更新

ScalaFX 只是 JavaFX 的包装器:每个 JavaFX 类型都有对应的 ScalaFX类型。 ScalaFX 提供隐式转换函数来无缝转换,比如说,JavaFX TitledPaneScalaFX TitledPane,反之亦然。但是,两组对象之间没有继承关系。也就是说,JavaFX TitledPaneScalaFX TitledPane 没有类型关系。因此,在两组对象之间进行转换是一个复杂的过程。

如果您希望能够正确地转换 titled.content 以便更直接地访问内容的 height 属性,您需要获取 属性的值和结果的显式模式匹配对象的 JavaFX 版本,如下所示:

import javafx.scene.control.{ListView => JFXListView}
import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.beans.property.DoubleProperty
import scalafx.beans.value.ObservableValue
import scalafx.collections.ObservableBuffer
import scalafx.event.ActionEvent
import scalafx.scene.Scene
import scalafx.scene.control.cell.TextFieldListCell
import scalafx.scene.control.{Button, ListView, TitledPane}
import scalafx.scene.layout.BorderPane

object TitledPaneEndOfExpansion extends JFXApp {
  var expandedHeight: Double = _
  val data: ObservableBuffer[String] = new ObservableBuffer[String]() ++= List("some", "content", "for", "testing")

  stage = new JFXApp.PrimaryStage {
    title = "JavaFX: edit after rendering test"

    val list: ListView[String] = new ListView[String](data) {
      editable = true
      cellFactory = TextFieldListCell.forListView()
      height.onChange { (source: ObservableValue[Double, Number], oldValue: Number, newValue: Number) =>
        expandedHeight = titled.content.value match {
          case lv: JFXListView[_] => lv.height.value
          case _ => {
            throw new RuntimeException(s"Unexpected content type: ${titled.content.getClass.getCanonicalName}")
          }
        }
        println("old height is: " + oldValue.doubleValue() + "   new height is: " + newValue.doubleValue())
        if (newValue.doubleValue() == expandedHeight) {
          edit(1)
        }
      }
    }

    val titled: TitledPane = new TitledPane {
      text = "titled"
      content = list
    }

    scene = new Scene {
      root = new BorderPane {
        center = titled
        bottom = new Button() {
          text = "edit cell 1"
          onAction = { _: ActionEvent => list.edit(1) }
        }
      }
    }
    expandedHeight = titled.content.value match {  //set to 400
      case lv: JFXListView[_] => lv.height.value
      case _ => throw new RuntimeException(s"Unexpected content type: ${titled.content.getClass.getCanonicalName}")
    }
    list.edit(1)
  }
}

如果您没有任何其他方式引用 list 对象,那将是您唯一的选择。