カスタム ScalaFX コントロールを作成する正しい方法は何ですか? 私は Swing と Scala Swing の出身です。カスタム コンポーネントはComponent
orを拡張するだけで簡単に作成できPanel
ます。しかし、ScalaFX の を拡張しようとすると、JavaFXデリゲートControl
なしでは拡張できません。Control
ScalaFX クラスの代わりにベースの JavaFX クラスを拡張して、カスタム ScalaFX コンポーネントを作成する必要がありますか?
1 に答える
一般的に言えば、次のことを行います。
- カスタム JavaFX コントロールを作成します。
- 次に、オプションで、デフォルトのものと同じモデルでカスタム ScalaFX ラッパーを作成します。一部の ScalaFX 機能 (バインディングなど) は、特定の ScalaFX ラッパーがなくても正常に動作することに注意してください。ここでいくつかの例を確認できます。
カスタム JavaFX コントロールを作成するには、最初にチェックアウトするリソースはこの Oracle チュートリアルですが、このブログ記事はさらに詳しく説明しています。ControlsFXやJFXtrasなどのオープン ソース プロジェクトには、コントロールの例がたくさんあります。
明らかに、これらのリソースはすべて、Java でそれを行う方法を示しています。Scala でそれを実行できなかった理由はわかりません (ScalaFX クラスではなく JavaFX クラスを使用している限り) - しかし、それに関するドキュメントを見つけることができなかったので、おそらくJava でコントロールを作成する方が安全です。
編集: ScalaFX ラッパー クラスを使用した単純なカスタム JavaFX コントロールの 2 つの例をgithubに掲載しました。1 つのバージョン はYieldingSlider
、クラスを拡張する単一の Java クラスSlider
です。もう 1 つのバージョン はFxmlYieldingSlider
、基本的に同じものですが、FXML ファイルとコントローラー クラスを使用してコントロールを構築する方法を示しています。このプロジェクトからビルドされた JAR ファイルは Scene Builder 2.0 にインポートできるため、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 コードに醜いハックを入れて、 のように処理することcustomscalafx.*
でしscalafx.*
た。しかし、これは ScalaFXML を使用する場合にのみ問題になります。
編集 2:私はそれに取り組んでいますが、これは Java ではなく inScala で記述された同じ 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
}