JavaFX Script 高级特性

JavaFX Script 语言简介

JavaFX 是 Sun 推出的一套基于 Java 技术的产品家族,其目的是为从桌面机、移动设备、机顶盒到蓝光光盘等提供一致的用户体验。 JavaFX 目前包含 JavaFX Script 和 JavaFX Mobile 。 JavaFX Script 可以用来高效的创建富媒体和交互性很强的应用。 JavaFX 是 Adobe Flex 和 Microsoft Silverlight 的有力竞争者。本文通过具体的例子介绍了 JavaFX Script 语言本身的一些高级特性。这些特性包括块表达式、范围(Range)表达式、序列的修改、触发器(Trigger)和数据绑定。另外还结合具体的应用,讨论了 JavaFX Script 中的一些高级话题,包括创建复杂的用户界面、实现动画效果、JavaFX Script 应用的部署和在一个独立的 Java 应用程序中嵌入 JavaFX Script 等。

目前 JavaFX Script 语言本身在不断的变化之中,本文中对 JavaFX Script 语法的说明和实例,均基于 JavaFX Script 在 2008 年 7 月 21 号的版本。开发环境采用 NetBeans 6.1,并安装 JavaFX Script 插件。请从 参考资源 中下载相关的工具。

JavaFX Script 高级特性

下面具体介绍 JavaFX Script 语言的一些高级特性。

块表达式

JavaFX Script 中的块表达式是包含在 {} 中的一系列用分号分隔的语句。如果块表达式中的最后一个语句是表达式的话,那么这个块表达式的值就是最后这个表达式的值;否则该块表达式的值是 Void 类型。块表达式适合于那些在代码中只出现一次的计算逻辑。因为只出现一次,可以不用把这样的逻辑封装在一个 function 中。而块表达式又可以把这部分逻辑的代码与其它部分区别开来。在 代码清单 1 中,对于薪水的计算逻辑被封装在一个块表达式中。


清单 1. 块表达式示例

var baseSalary = 10000; 
 var salary = { 
    12 * (baseSalary + 1000) + 2 * baseSalary 
 }; 
 System.out.println(salary);

范围表达式

熟悉其他动态语言,如 Python、Ruby 和 Groovy 的人对于范围(Range)表达式可能并不陌生。 JavaFX Script 也引入了同样的范围表达式,可以用来定义一个序列。不过在使用方式与其他语言有些不同。

在 JavaFX Script 中可以通过 [number1..number2] 来定义一个序列。这里需要注意的是两个边界数字 number1 和 number2 都是包含在序列里面的。如 [0..5] 包含 0,1,2,3,4,5 这六个数字。这点和 Ruby 里面的 number1...number2 和 Groovy 里面的 number1..number2 是一样的。默认情况下,范围中的数字之间的间隔是 1,可以通过在 number2 后面加上显式的 step 来声明间隔。比如 [0..9 step 3] 包含的数字是 0,3,6,9 。

当 number1 大于 number2 的时候,可以通过指定值为负数的 step 来生成降序排列的序列。如 [5..0 step -1] 包含的数字是 5,4,3,2,1,0 ;如果不指定 step,或是 step 的值为正数的话,生成的序列实际上是空的。如 [5..0] 是个空的序列。

序列的修改

JavaFX Script 提供了两个强大的操作符 insertdelete 来对序列进行操作。

insert 语句的语法如 代码清单 2 中所示。


清单 2. insert 语句的语法

insert x into seq 
 insert x before seq[idx] 
 insert x after seq[idx]

代码清单 2 列出的语法中可以看到,insert 语句可以往序列中的指定位置插入新元素。使用 insert x into seq 会把 x 添加到序列末尾。而 insert x before seq[idx]insert x after seq[idx] 则分别可以把新元素插入到 seq[idx] 所对应的元素的前面和后面。

delete 的语法如 代码清单 3 中所示。


清单 3. delete 语句的语法

delete seq 
 delete x from seq 
 delete seq[idx] 
 delete seq[a..b]

代码清单 3 列出的四种用法中,第一种会删除整个序列;第二种和第三种都会删除指定元素,不同的是第二种需要指定元素的值,而第三种需要指定其序号;第四种是用来删除序列中给定范围之内的全部元素的。

触发器

JavaFX Script 提供类似 SQL 中触发器的机制来处理数据的变化。如果在某个属性上声明了触发器,那么当它的值发生变化的时候,触发器就会被触发,预先定义的逻辑就会被执行。代码清单 4 中给出了触发器的一个实例。


清单 4. 触发器示例

 

代码清单 4 中可以看到,当属性 address 的值发生改变时,会输出相应的提示信息。该属性的旧值和新值都可以得到,并在相应的处理逻辑代码中使用。触发器不仅可以用于单值属性,也可以用于序列上。代码清单 5代码清单 6 中给出的两个例子分别演示了如何在序列中插入和删除元素之后得到通知。


清单 5. 序列中插入新元素的触发器

清单 6. 序列中删除元素的触发器

数据绑定

JavaFX Script 中,属性初始化的时候,可以通过 bind 将属性的值和某个表达式绑定起来,当该表达式所引用的对象发生变化时,该属性的值也会自动更新。方法也可以通过 bound 来设成绑定的。以 代码清单 7 为例,方法 getPercent 在其内部的变量的值发生改变的时候,会自动重新求值。属性 currentValue 的值的改变首先使得方法 getPercent 的值发生改变,然后再使得 percent 的值发生变化。这一切都是自动的。


清单 7. 数据绑定示例

 

上面介绍了 JavaFX Script 语言本身的一些高级特性,下面将以实例的方式讨论 JavaFX Script 开发中的几个重要的主题。

创建复杂用户界面

用过 Swing 的人可能都有个感觉,那就是用 Swing 创建用户界面的时候,不仅编写起来麻烦,代码也比较冗长。使用 JavaFX Script,可以帮助你更快更简单的创建用户界面。 JavaFX Script 简化用户界面的创建的能力主要体现在三个方面:

声明式创建图形用户界面(GUI)组件
程序员可以在代码中以声明的方式编写 GUI 组件,这些 GUI 组件的层次结构就反映了实际的用户界面的布局。声明式的方式大大减少了创建用户界面所需要的代码量。程序员只需要看到代码的组织结构,就可以想象出实际的运行效果。
数据绑定和增量式求值
数据绑定和增量式求值使得组件的创建和配置变得更加容易,应用本身的数据和 GUI 组件可以自动同步。比如在用户界面上有个标签( javafx.ext.swing.Label)用来显示雇员的名字,只需要将标签的 text 属性绑定到雇员数据的 name 属性上。当雇员数据发生变化的时候,该标签会自动更新。
触发器
触发器使得当某个属性的值发生变化的时候,可以执行相应的处理逻辑。这非常类似 Java Bean 中的 PropertyChangeListenerPropertyChangeEvent 的组合。但是触发器的创建和使用更加简单。

 

在 JavaFX Script 中使用 Swing 的 GUI 组件是很容易的。 JavaFX Script 在 javafx.ext.swing 包中提供了很多常用的 Swing 组件的封装。这些组件可以在 JavaFX Script 代码中直接以声明式的方式使用。另外一些复杂的组件,如 JTableJTree,目前在 JavaFX Script 中还不可以直接使用。在这种情况下,需要继承 javafx.ext.swing.Component 类并实现该类的抽象方法 createJComponent()。该方法返回一个 JComponent 对象。

下面以一个具体的实例来说明如何创建复杂用户界面。该应用是一个 RSS 订阅源(Feed)的阅读器,它可以解析 OPML 文件并在左侧以一个树形结构来显示所有的订阅源。当点击某个订阅源时,可以在右侧看到该订阅源的内容。显示所有订阅源的是左侧的 OmplViewer,显示订阅源内容的是右侧的 FeedViewer。该应用的截图见 图 1


图 1. RSS 订阅源阅读器

 

由于 RSS 订阅源阅读器的代码比较多,在本文中将只介绍其中的重点,完整的代码请 下载

继承javafx.ext.swing.Component

在上面提到,一些复杂的 Swing 组件,目前需要通过继承 javafx.ext.swing.Component 的方式来使用。在 RSS 订阅源阅读器中,左侧显示所有订阅源的组件是 JTree。类 OpmlViewer 继承自 javafx.ext.swing.Component,并实现其createJComponent()方法,该方法创建了一个JTree 对象,并包装在一个 JScrollPane 中。该类中的其它代码都与 JTree 对象交互。具体见 代码清单 8


清单 8. 继承 javafx.ext.swing.Component 示例

public class OpmlViewer extends Component { 
    private attribute tree : JTree; 
	
    protected  function createJComponent(): JComponent {   
        tree = new JTree(); 
        return new JScrollPane(tree); 
    } 
 }

触发器的使用

OpmlViewer 有一个属性 opmlFilePath 用来表示 OPML 文件的路径。当该属性的值发生改变的时候,OpmlViewer 就需要显示该 OPML 文件中的所有订阅源。这是通过在属性 opmlFilePath 上创建一个触发器来实现的。具体见 代码清单 9


清单 9. OpmlViewer 中的触发器使用

public attribute opmlFilePath : String on replace 
    oldPath = newPath { 
        if (newPath !=  null and  not "".equals(newPath.trim())) {   
            setOpmlFilePath(newPath); 
        } 
    }

数据绑定的使用

数据绑定在这个应用中主要用来协调左侧的 OmplViewer 和右侧的 FeedViewerFeedViewer 的属性 feedUrl 绑定到 OmplViewer 的属性 selectedFeedUrl。这样当用户选择了某个订阅源的时候,OmplViewer 的属性 selectedFeedUrl 的值会发生变化,FeedViewer 的属性 feedUrl 的值也会随之变化。FeedViewer 就会去加载新的订阅源内容了。具体见 代码清单 10


清单 10. OpmlView 和 FeedViewer 之间的数据绑定

var opmlViewer = OpmlViewer { 
    opmlFilePath : "c://google-reader-subscriptions.xml", 
    hmax : 200 
    hmin : 100 
 }; 
 var feedViewer = FeedViewer { 
    feedUrl : bind opmlViewer.selectedFeedUrl 
 };

上面介绍了如何在 JavaFX Script 中创建复杂的用户界面,下面将涉及 JavaFX Script 中一个有趣的话题,那就是实现动画效果。

创建动画

使用 JavaFX Script 来创建动画是一件非常容易的事情。 JavaFX Script 所采用的动画方式叫“关键帧动画(key frame animation)”。这种动画是通过一系列的场景的转换来完成的,而场景则是由特定时间点上的关键帧来表示的。程序员只需要以声明式的方式定义这些关键帧,系统会自动的完成剩下的工作。“关键帧动画”又分成两种,离散型和插值型。两者的区别在于:对于后者,系统会用特定的插值函数来计算每个关键帧之间的中间状态。 JavaFX Script 中的动画主要是依靠 javafx.animation.Timelinejavafx.animation.KeyFramejavafx.animation.KeyValue 这三个类来完成的。

这三个类的基本说明如下:

javafx.animation.Timeline
Timeline 可以理解为动画的时间轴。所有的动画都是围绕一个时间轴来运行的。
javafx.animation.KeyFrame
KeyFrame 是时间轴上的关键帧。每个关键帧都与时间轴上的某个时间点关联起来。每个关键帧可以包含一系列的 KeyValue 和子时间轴,以及一个动作( action)。子时间轴的起始时间点是相对于其父时间轴的。从而使得时间轴可以嵌套起来,形成层次结构。动作则是当关键帧对应的时间点到达的时候,所执行的一段代码。
javafx.animation.KeyValue
KeyValue 给出了某个属性在当前关键帧中的值,以及一个用来计算在之前的关键帧中该属性的值与当前值之间的中间值的方法。关于 KeyValue 的具体介绍请看下节。

 

KeyValue 的用法

下面用一个例子来具体说明 KeyValue 的用法,见 代码清单 11


清单 11. KeyValue 的用法

代码清单 11 中创建了一个无限运行的 Timeline,该 Timeline 包含 3 个关键帧,所关联的时间点分别是 1 秒、1.5 秒和 2 秒。在这 3 个关键帧所关联的时间点到达的时候,都会执行 output 方法,该方法会打印出 simpleVar 的值。需要说明的是在第三个关键帧上,通过 values 声明了 simpleVar 在该关键帧中的值是 200,并且是通过线性插值(Interpolator.LINEAR)的方式来计算中间值。因此,在 1 秒和 1.5 秒的关键帧中,simpleVar 的值就是根据线性插值的方式计算出来的。

动画实例

下面通过一个实例来说明如何使用 JavaFX Script 来编写动画。在这个动画实例中,将演示地球自转和月球围绕地球的转动。动画的演示效果见 图 2 所示。需要说明的是 图 2 中的地球和月球的大小以及月球的运行轨道均为示意。


图 2. 地球自转和月球围绕地球运转的动画

 

该动画的源代码如代码清单 12所示。


清单 12. 地球和月球运行示意动画的源代码

代码中创建了两个 Timeline,一个用来表示地球的自转,另外一个用来表示月球围绕地球的转动。表示地球自转的动画使用的是离散型“关键帧动画”。该 Timeline earthTimeline 包含 7 个关键帧,每个帧的间隔是 300 毫秒。当每个帧所关联的时间点到达的时候,所执行的动作是将 currentEarthImage 的值设成该帧对应的地球的一张图片。当 currentEarthImage 的值发生改变的时候,ImageView 就会显示那张图片。
表示月球围绕地球转动的动画使用的是插值型“关键帧动画”。该 Timeline moonTimeline 的效果是使得代表月球的图片按照一个圆形来变换位置。该 Timeline 包含 8 个关键帧,分别表示圆周上的 8 个位置。当每个帧所关联的时间点到达的时候,所执行的动作是 moonXmoonY 的值设成计算出来的月球的图片在窗口中的位置。通过 Transform.translate(moonX, moonY) 这样的一个变换来改变图片的位置。插值型“关键帧动画”指的是,程序中声明了 8 个关键帧,系统会自动按照指定的函数来计算图片在中间状态的位置。这里使用的插值函数是线性插值。

值得注意的是,如果使用 代码清单 12 中的方式,运行起来之后,月球的图片的运动并不是一个圆形,而是一个正八边形。这是由于以线性插值的方式计算出来的中间状态的位置与实际需要的弧形有较大的偏差。而目前 JavaFX Script 并没有提供弧形的插值函数。所以一个更好的选择是使用离散型“关键帧动画”,并且提供更多的关键帧,这样使得月球图片的运行轨迹更加平滑。代码清单 13 中给出了新的关键帧的生成方法。


清单 13. 更平滑的关键帧生成方法

代码清单 13 中可以看到,一共定义了 36 个关键帧,每个帧对应的 moonXmoonY 的值都是通过计算得出来的。使用了上面的 36 个关键帧之后,运行起来可以看到月球图片的更好的动画效果。

在讨论完 JavaFX Script 中的动画之后,下面将讨论如何部署 JavaFX Script 应用。

 

部署 JavaFX Script 应用

在完成 JavaFX Script 应用的编写和调试之后,就可以部署该应用了。 JavaFX Script 的应用的部署方式主要有两种,分别是使用 Java Web Start 和使用 Open JavaFX 编译器。下面具体介绍这两种部署方式。

使用 Java Web Start

Java Web Start 是从 JRE 5.0 开始被引入到 Java 运行环境(JRE)中来的。它使得可以从 Web 下载和运行 Java 程序。当本机安装了 5.0 及以上版本的 JRE 后,如果通过浏览器访问内容类型(Content type)是 application/x-java-jnlp-file 的 URL 时,Java Web Start 会自动运行,并下载相应的 JNLP 文件,来启动 Java 应用。

JNLP 的文件内容可以参考 代码清单 14。该 JNLP 文件可以用来启动前面提到的 JavaFX Script 应用“ RSS 订阅源阅读器”。


清单 14. “ RSS 订阅源阅读器”的 JNLP 文件

 

需要说明的是,在使用 Java Web Start 时,所有的 jar 包都必须经过数字签名。可以使用 JDK 中自带的 jarsigner 这个工具。

使用 OpenJFX 编译器

OpenJFX 是 JavaFX 开源社区中的一个孵化项目,它的作用是将 JavaFX Script 编译成 JVM 上可以直接运行的 class 文件(字节码)。从 参考资源 中下载了 OpenJFX 的编译器之后,通过运行 javafxc 命令就可以将一个 JavaFX Script 文件编译成一系列 Java class 文件。再使用 javafx 命令就可以运行该应用。

在独立的 Java 应用中嵌入 JavaFX Script

JDK 6.0 中引入了 JSR 223(Scripting 框架),使得可以在 JVM 上执行 JavaScript、Python、Ruby 和 JavaFX Script 等动态语言。如果你的 JavaFX Script 是作为一个独立的 Java 应用的一部分的话,使用 JSR 223 可以很容易将 JavaFX Script 嵌入其中。

下面以一个简单计算器的例子来说明如何使用 JSR 223 来将 JavaFX Script 嵌入 Java 应用中。该简单计算器可以接受用户输入的算术表达式并将计算结果告诉用户。算术表达式可以包含加、减、乘、除和括号等。如果使用 Java 来计算算术表达式的值话,需要对表达式进行分析,拆分成可以识别的符号,再进行运算。而如果使用 JavaFX Script 的话,只需要将整条表达式交给 JavaFX 引擎去求值即可。因此,整个程序由 Java 来处理用户的输入和输出结果,使用 JavaFX Script 来求值。具体见 代码清单 15


清单 15. Java 应用中嵌入 JavaFX Script 的示例

 

需要说明的是,必须将 OpenJFX 编译器中的 javafxc.jar 这个包添加进来,否则会出现找不到ScriptEngine的错误。这是由于 JavaFX Script 的引擎是由 javafxc.jar 包中的 com.sun.tools.javafx.script.JavaFXScriptEngineFactory 类来实现的。

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值