JavaFX学习笔记(02)(DSL构建)

来源:互联网 发布:神回复 知乎 编辑:程序博客网 时间:2024/06/06 08:59

为了能够更深入的了解JavaFX类库,我将会用Scala实现一个JavaFX的DSL。又开始挖大坑了,希望这次能够填完

这个DSL会是什么样呢?如果你对C++略有涉猎,那么肯定对C++中流插入和流提取不陌生。我们将要实现的这个DSL,就会围绕着<<插入符展开。


首先,我们面临的第一个问题,就是使用Scala的类继承Application类会报错(听说用单例对象继承就可以了,不过当时我没有想到这一点)。于是我写了自己的一个SApplication类作为所有Scala所写的JavaFX应用的基类:

package sjavafx.applicationimport javafx.application.Application/**  * Created by Administrator on 2016/10/3.  */abstract class SApplication extends Application {    def run = Application.launch()    def run(args: Array[String]) = Application.launch(args: _*)    def run(args: String*) = Application.launch(args: _*)    def run(appClass: Class[_ <: Application], args: String*) = Application.launch(appClass, args: _*)}

这个类只是Application类在Scala中简单的封装,这个类的子类将可以直接成为应用程序类而不需要再去写main()方法。

为了便于在构建DSL过程中进行调试,我从网络上(JavaFXChina)找到了一个简单的例子,然后拿Scala进行了小幅度的改写:

import sjavafx.application.SApplicationimport javafx.event.ActionEventimport javafx.event.EventHandlerimport javafx.scene.Sceneimport javafx.scene.control.Buttonimport javafx.scene.layout.StackPaneimport javafx.stage.Stageclass Text extends SApplication {    def start(primaryStage:Stage) {        val btn = new Button()        btn.setText("Say 'Hello World'")        btn.setOnAction(new EventHandler[ActionEvent]() {            def handle(event:ActionEvent) {                println("Hello World!")            }        })        val root = new StackPane()        root.getChildren().add(btn)        val scene = new Scene(root, 300, 250)        primaryStage.setTitle("Hello World!")        primaryStage.setScene(scene)        primaryStage.show()    }}

现在我们就要先围绕着这个测试类来对JavaFX进行包装。

object SApplication {}

首先我们在定义了一个SApplication类的伴生对象,里面将要存放一些十分重要的隐式转换等核心功能。
现在我们定义一个特质:Insertionable:
这里写图片描述
这个特质将是整个类库里最核心的部分之一。

object SApplication {    val VarPool = new mutable.HashMap[String, Insertionable]    object Name}

然后我们在这个基础上就可以进行一定的修改了:

object SApplication {    val VarPool = new mutable.HashMap[String, Insertionable]    /**      * 用法:ins :Insertionable << Type << theType      * 描述:转换Insertionable类型变量为theType所描述类型.      */    object Type    object tSButton/*sjavafx.scene.control.SButton*/    /**      * 用法:ins :Insertionable << Name << name:String      * 描述:设定变量名.      */    object Name}
trait Insertionable {    def <<(name: Name.type) = new Ins {        def <<(name: String) = {            VarPool(name) = ins            ins        }    }    def <<(ty: Type.type) = new Ins {        def <<(sbutton: tSButton.type) = ins match{            case sb :SButton => sb            case _ => throw new ClassCastException        }    }    class Ins(val ins: Insertionable = this)}

然后我们加入了新的类SButton,这个类是Button类的子类,并实现了Insertionable接口:

class SButton() extends Button() with Insertionable {    def this(str: String) = {        this()        setText(str)    }}

现在我们的测试类已经可以加入<<运算符了!虽然暂时还没有做什么实质性的事情,但是这也证明了我们的尝试是成功的:

import sjavafx.application.SApplicationimport javafx.event.ActionEventimport javafx.event.EventHandlerimport javafx.scene.Sceneimport javafx.scene.layout.StackPaneimport javafx.stage.Stageimport sjavafx.application.SApplication._import sjavafx.scene.control.SButtonclass Text extends SApplication {    def start(primaryStage:Stage) {        val btn = new SButton <<                Name << "btn" <<                Type << tSButton        btn.setText("Say 'Hello World'")        btn.setOnAction(new EventHandler[ActionEvent]() {            def handle(event:ActionEvent) {                println("Hello World!")            }        })        val root = new StackPane()        root.getChildren.add(btn)        val scene = new Scene(root, 300, 250)        primaryStage.setTitle("Hello World!")        primaryStage.setScene(scene)        primaryStage.show    }}

看样子我们的程序还是很成功的:这里写图片描述

0 0