创建自定义ScalaFX控件的正确方法究竟是什么?我来自Swing和Scala Swing,只需通过扩展Component
或Panel
即可创建自定义组件。但是当我尝试扩展ScalaFX Control
时,我无法在没有JavaFX Control
委托的情况下扩展它。我应该通过扩展基本JavFX类而不是ScalaFX类来创建自定义ScalaFX组件吗?
答案 0 :(得分:4)
一般来说,您需要:
要创建自定义JavaFX控件,要检出的第一个资源是this Oracle tutorial,但this blog post会更进一步。像ControlsFX和JFXtras这样的开源项目提供了大量的控件示例。
显然,所有这些资源都显示了如何在Java中完成它。我没有看到为什么你不能在Scala中做到这一点(只要你使用JavaFX类而不是ScalaFX类) - 但我无法找到任何关于它的文档,所以我猜它可能是在Java中创建控件更安全。
编辑:我已经使用ScalaFX包装器类提供了on github两个简单自定义JavaFX控件的示例。一个版本YieldingSlider
是一个扩展Slider
类的Java类;另一个版本FxmlYieldingSlider
基本上是相同的,但它显示了如何使用FXML文件和控制器类构造控件。请注意,可以在Scene Builder 2.0中导入从此项目构建的JAR文件,以便Scene Builder可以使用FXML中的<YieldingSlider>
和<FxmlYieldingSlider>
控件。
这是简单版本的样子。
JavaFX控件:
package customjavafx.scene.control;
import javafx.scene.control.Slider;
import javafx.scene.input.MouseEvent;
public class YieldingSlider extends Slider {
public YieldingSlider() {
addEventFilter(MouseEvent.MOUSE_PRESSED, event -> lastTimeMousePressed = System.currentTimeMillis());
}
public YieldingSlider(final double min, final double max, final double value) {
this();
setMin(min);
setMax(max);
setValue(value);
}
private long lastTimeMousePressed = 0;
public boolean mouseWasPressedWithinLast(final long t) {
return (System.currentTimeMillis() - lastTimeMousePressed) <= t;
}
}
ScalaFX包装器:
package customscalafx.scene.control
import scala.language.implicitConversions
import customjavafx.scene.{control => jfxsc}
import scalafx.scene.control.Slider
object YieldingSlider {
implicit def sfxSlider2jfx(v: YieldingSlider) = v.delegate
}
class YieldingSlider(override val delegate: jfxsc.YieldingSlider = new jfxsc.YieldingSlider) extends Slider {
/** Constructs a Slider control with the specified slider min, max and current value values. */
def this(min: Double, max: Double, value: Double) {
this(new jfxsc.YieldingSlider(min, max, value))
}
}
可用于FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import customjavafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<YieldingSlider AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" />
</children>
</AnchorPane>
或者在ScalaFX DSL中:
package guilgaly.fxtest.mp3player
import customscalafx.scene.control.YieldingSlider
import scalafx.application.JFXApp
import scalafx.scene.Scene
object TestApp extends JFXApp {
stage = new JFXApp.PrimaryStage {
scene = new Scene {
content = new YieldingSlider
}
}
}
最后,请注意,如果您将它与ScalaFXML一起使用,它将无法在控制器中正确注入,因为ScalaFXML会查找其包以scalafx.*
开头的类(并且期望相同的JavaFX类在同一个包中,但是盯着javafx.*
)。但是,如果使用从javafx.*
开始的包,则无法在Scene Builder中导入控件。我的解决方案是在ScalaFXML代码中添加一个不正常的hack,以便它像customscalafx.*
那样处理scalafx.*
。但这只是使用ScalaFXML时的一个问题。
编辑2:虽然我正在使用它,但这是用斯卡拉而不是Java编写的相同JavaFX控件。它的工作原理相同,如果需要,可以用类似的ScalaFX包装器包装。
package customjavafx.scene.control
import javafx.event.EventHandler
import javafx.scene.control.Slider
import javafx.scene.input.MouseEvent
class ScalaYieldingSlider extends Slider{
def this(min: Double, max: Double, value: Double) = {
this()
setMin(min)
setMax(max)
setValue(value)
}
// Support for Java 8 SAMs (lambdas) is still experimental in Scala 2.11.
// I used the old-school anonymous class instead.
addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler[MouseEvent] {
def handle(event: MouseEvent): Unit = lastTimeMousePressed = System.currentTimeMillis
})
private var lastTimeMousePressed: Long = 0
def mouseWasPressedWithinLast(t: Long): Boolean =
(System.currentTimeMillis - lastTimeMousePressed) <= t
}