教程-GroovyFX

发布于:2021-01-30 10:05:20

0

51

0

groovy groovyfx java javafx

GroovyFX是一个使编写javafx2.x应用程序更加简单和自然的库。GroovyFX利用Groovy的Builder模式的强大功能,使开发人员能够以简洁的声明式风格进行编写。以这种方式构造ui的能力使得可视化正在构建的用户界面变得更容易。简短易懂的代码也易于维护和扩展。GroovyFX完全支持JavaFX的所有高级特性,如控件、布局、图形、动画、声音、视频、图表等等。

GroovyFX的0.2版本可以作为Sonatype的Maven存储库的快照提供。其组ID为网址:org.codehaus.groovyfx它的工件ID就是groovyfx。在本文中,我将使用最新版本:0.2-SNAPSHOT。

在撰写本文时,Oracle的JavaFX团队正在完成JavaFX2.1的工作。我预计这一版本将在本文发布后不久发布,因此本文中的所有代码示例都将使用groovyfx0.2-SNAPSHOT,它是GroovyFX的版本,跟踪javafx2.1中的更改。

我最近发表了一篇博客文章,详细介绍了如何在Gradle项目和带有@Grab注释的Groovy脚本中使用GroovyFX。或者,您可以直接从GitHub克隆项目,并按照项目的GitHub页面上的说明进行构建(确保您正在使用develop分支)。

构建场景

JavaFX用户界面是通过构建场景图来创建的。因此,GroovyFX有一个SceneGraphBuilder类,它包装了javafxapi类,并使用Groovy的生成器语法使它们可用。但是,很少直接实例化和使用SceneGraphBuilder对象。相反,您通常会使用GroovyFX类的静态start方法,向它传递一个执行GroovyFX代码的闭包。清单1所示的示例说明了这一点,它显示了一个基本的GroovyFX“Hello,World”程序。

清单1:Hello World

import groovyx.javafx.GroovyFX

GroovyFX.start {
 stage(title: "GroovyFX Hello World", visible: true) {
   scene(fill: black, width: 530, height: 300) {
     hbox(padding: 80) {
       text(text: "Groovy", style: "-fx-font-size: 80pt") {
         fill linearGradient(endX: 0, stops: [palegreen, seagreen])
       }
       text(text: "FX", style: "-fx-font-size: 80pt") {
         fill linearGradient(endX: 0, stops: [cyan, dodgerblue])
         effect dropShadow(color: dodgerblue, radius: 25, spread: 0.25)
       }
     }
   }
 }
}

JavaFX中的主要应用程序容器是stage。当作为桌面应用程序运行时,它对应于应用程序的窗口;当作为浏览器中的小程序运行时,它对应于应用程序的内容区域。stage包含一个场景,该场景保留对场景图根节点的引用,该节点是一个数据结构,定义了应用程序显示的内容。因此,每个JavaFX应用程序都由一个Stage、一个场景和一个或多个场景图节点类组成。在清单1所示的GroovyFX代码中很容易找到这种结构,其中场景图的根是JavaFX Hbox类的一个实例。HBox是一个布局节点,它以水平线排列其子节点。清单1显示了根HBox节点有两个子文本节点,它们并排显示字符串“Groovy”和“FX”,如图1所示。

 

细心的读者无疑已经推断出GroovyFX中使用的命名模式。groovyfxbuilder语法中的节点与其对应的JavaFX类具有相同的名称,类名的第一个大写字母转换为小写。因此javafxstage类在GroovyFX中被称为Stage。类似地,LinearGradient变为LinearGradient,HBox变为HBox,Button变为Button,依此类推。

这些类的属性可以通过将属性名称用作映射中的键来设置,映射作为参数传递给生成器节点。下面的代码创建了一个stage,其title属性设置为“GroovyFX Hello World”。

GroovyFX还提供了一些方便的快捷方式来声明场景图节点和属性。请注意清单1中的linearGradient声明。GroovyFX允许您使用快捷方式,例如按名称引用颜色,而不是使用静态值,例如“颜色:青色”. 此外,您可以简单地传递一个颜色数组作为lineargradent的stops属性,GroovyFX将使用这些颜色自动创建等间距的渐变停止。这些方便的快捷方式使编写GroovyFX代码变得干净快捷。

控件和布局

JavaFX附带了一套完整的现代UI控件,如按钮、标签、文本字段和复选框。还包括更复杂的控件,如ListView、TreeView和TableView。JavaFX还包括一组功能强大的布局,如HBox、VBox、BorderPane、TilePane和功能强大且灵活的GridPane。所有这些控件和布局窗格都只是JavaFX场景图中的节点。如清单1所示,任何作为布局窗格子级的场景图节点都将自动由该布局窗格设置其位置和大小(例如作为Hbox子级的两个文本节点)。从技术上讲,在布局期间还有其他因素起作用,例如节点的托管属性的值以及节点是否可调整大小,但在绝大多数情况下,您可以简单地假设布局窗格将控制其子节点的大小和位置。

某些布局窗格需要其他信息来布局其子节点。BorderPane需要知道节点是应该放置在布局空间的北、南、东、西还是中心。GridPane需要知道在哪个行和列中放置节点。如果您使用的是Java,则需要通过其他方法调用来设置这些约束。在GroovyFX中,这些约束可以直接传递到节点的属性映射,GroovyFX将负责为您调用约束方法。这允许您将布局约束保留在声明节点的同一位置,并使布局更易于理解。清单2中有一个这样的例子,其中GroovyFX使用放置在GridPane中的各种控件来构建一个简单的表单。

清单2:

import static groovyx.javafx.GroovyFX.start

start {
 stage(title: "GridPane Demo", width: 400, height: 500, visible: true) {
   scene(fill: groovyblue) {
     gridPane(hgap: 5, vgap: 10, padding: 25, alignment: "top_center") {
       columnConstraints(minWidth: 50, halignment: "right")
       columnConstraints(prefWidth: 250, hgrow: 'always')

       label("Please Send Us Your Feedback", style: "-fx-font-size: 18px;",
             textFill: white, row: 0, columnSpan: 2, halignment: "center",
             margin: [0, 0, 10]) {
         onMouseEntered { e -> e.source.parent.gridLinesVisible = true }
         onMouseExited { e -> e.source.parent.gridLinesVisible = false }
       }

       label("Name", hgrow: "never", row: 1, column: 0, textFill: white)
       textField(promptText: "Your name", row: 1, column: 1 )

       label("Email", row: 2, column: 0, textFill: white)
       textField(promptText: "Your email address", row: 2, column: 1)

       label("Message", row: 3, column: 0, valignment: "baseline",
             textFill: white)
       textArea(prefRowCount: 8, row: 3, column: 1, vgrow: 'always')

       button("Send Message", row: 4, column: 1, halignment: "right")
     }
   }
 }
}

清单2展示了GroovyFX的更多便利。您将注意到,GridPane的对齐方式设置为“topu center”。GroovyFX会发现您实际上引用的是枚举值“位置顶部u中心因为GridPane的alignment属性是Pos类型。设置halignment为“center”时也是如此(HPos中心).

GroovyFX允许的另一个节省时间的便利是将事件处理程序定义为Groovy闭包。这可以在清单2中第一个label节点上声明的onMouseEntered和onmouseexted事件处理程序中看到。这两个事件处理程序使GridPane单元格的轮廓在鼠标悬停在标签上时可见。在GridPane中调试布局问题时,这是一个非常方便的技巧。清单2中的代码创建的表单如图2所示。

 

图形和动画

JavaFX还以其强大的图形和动画功能而闻名。GroovyFX在这些领域自然也有充分的支持。以清单3所示的代码为例,它是由Oracle的JavaFX团队编写的基于Java的彩色圆圈示例应用程序的GroovyFX翻译。

清单3:

import static groovyx.javafx.GroovyFX.start

start {
 def circles
 stage(title: 'GroovyFX ColorfulCircles', resizable: false, show: true) {
   scene(width: 800, height: 600, fill: 'black') {
     group {
       circles = group {
         30.times {
           circle(radius: 200, fill: rgb(255, 255, 255, 0.05),
                  stroke: rgb(255, 255, 255, 0.16),
                  strokeWidth: 4, strokeType: 'outside')
         }
         effect boxBlur(width: 10, height: 10, iterations: 3)
       }
       rectangle(width: 800, height: 600, blendMode: 'overlay') {
       def stops = ['#f8bd55', '#c0fe56', '#5dfbc1', '#64c2f8',
                    '#be4af7', '#ed5fc2', '#ef504c', '#f2660f']
       fill linearGradient(start: [0f, 1f], end: [1f, 0f], stops: stops)
     }
   }
 }

 parallelTransition(cycleCount: 'indefinite', autoReverse: true) {
   def random = new Random()
   circles.children.each { circle ->  translateTransition(40.s, node: circle, fromX: random.nextInt(800),
                         fromY: random.nextInt(600),
                         toX: random.nextInt(800),
                         toY: random.nextInt(600))
   }
 }.play()
}
 }
}
 

清单3演示了GroovyFX对JavaFX形状类(如Circle和Rectangle)的支持,以及对所有效果(如BoxBlur)的支持。使用Groovy循环构造(如30.times)来创建圆显示了使用Groovy的生成器语法的一个很好的优点:任何Groovy表达式或语句在生成器中都是有效的。这甚至包括这样的构造,比如if语句,它允许您有条件地构建节点。

动画无疑是JavaFX的优点之一。您可以定义自己的时间线或使用许多可用的预打包转换类中的一个。清单3演示了ParallelTransition(并行运行其他动画的转换)和TranslateTransition(更改节点的x、y坐标以便移动节点的转换)的使用。在这种情况下,对每个圆应用随机翻译,以便它们在屏幕上慢慢地漫游。此应用程序的输出如图3所示。

  

FXBindable注解

javafxscript(用于编写javafx1.x应用程序的语言)的一个非常强大的功能是它对绑定的内置支持。这个特性以属性和绑定API类的形式存在于基于Java的JavaFX2.x库中。这是一个流畅的API,允许您使用旧JavaFX脚本绑定功能的几乎所有功能。基于Java的API的缺点之一是定义绑定类能够使用的属性所需的样板代码量。以清单4所示的Person类为例。

清单4:

public class Person {
 private StringProperty firstName;
 public void setFirstName(String val) { firstNameProperty().set(val); }
 public String getFirstName() { return firstNameProperty().get(); }
 public StringProperty firstNameProperty() {
   if (firstName == null)
     firstName = new SimpleStringProperty(this, "firstName");
   return firstName;
 }

 private StringProperty lastName;
 public void setLastName(String value) { lastNameProperty().set(value); }
 public String getLastName() { return lastNameProperty().get(); }
 public StringProperty lastNameProperty() {
   if (lastName == null) // etc.
 }
}

此清单显示了在JavaFX中定义具有两个属性的类所需的典型样板。由于这些属性由StringProperty类支持,因此它们能够完全参与所有JavaFX绑定功能。但是,如果您使用该样板文件并将其乘以应用程序中每个类的每个属性,您可能会很快对它引入的大量额外代码感到苦恼,这些代码必须由您(或其他不幸的开发人员)进行调试和维护。

GroovyFX可以利用Groovy最强大的特性之一AST转换来解决这个问题。编写自定义AST转换允许您在编译时修改或插入代码,以减轻开发人员的编码负担。您所要做的就是用@FXBindable注释来注释一个标准的Groovy属性,GroovyFX将为您编写所有的样板代码!清单5显示了Person类在使用@FXBindable注释时的外观。信不信由你,这段代码与清单4所示的代码是等价的。

清单5

public class Person {
 @FXBindable String firstName;
 @FXBindable String lastName;
}

如果POGO中的所有属性都应该是可绑定的,那么可以对类进行注释,而不是对每个属性进行注释,如清单6所示。

清单6

@FXBindable
public class Person {
 String firstName;
 String lastName;
}

为了进一步了解@FXBindable和GroovyFX的绑定功能,我建议您查看GitHub上GroovyFX项目中的AnalogClockDemo。

是时候写一份完整的申请了?

当编写一个复杂的应用程序时,您通常需要一些支持,比如使用MVC设计模式、简单的线程,或者事件总线。这是Griffon的领域,一个易于使用的类似于Grails的框架,用于编写桌面Java应用程序。

您会很高兴地知道,迁移到Griffon并不意味着您需要放弃JavaFX的强大功能和GroovyFX的便利。Griffon有一个JavaFX插件,允许您使用GroovyFX编写应用程序的视图代码。griffonjavafx原型的GitHub页面上包含了入门说明。这个原型允许您快速启动并运行一个新的javafxgriffon应用程序。