65.9K
CodeProject 正在变化。 阅读更多。
Home

JavaFX 开发者指南摘录

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (4投票s)

2010年9月21日

CPOL

23分钟阅读

viewsIcon

21309

本章将从核心 JavaFX 平台到可用于构建和部署 JavaFX 应用程序的工具,提供对 JavaFX 的高级概述。

JavaFX-Developers-Guide.jpg

Kim Topley
由 Addison-Wesley Professional 出版
ISBN-10: 0-321-60165-3
ISBN-13: 78-0-321-60165-0

JavaFX 构建于 Java 之上,旨在简化创建可在从手机到桌面等各种设备上部署的应用程序的过程,并且在这些不同设备类型之间移动代码几乎不需要额外的工作。JavaFX 应用程序使用 JavaFX Script 编写,这是一种新颖易用的语言,本章的第二部分将介绍它。该平台的核心是 JavaFX 运行时库,应用程序可以使用它来构建用户界面、创建动画、从 RSS 和 Atom 源读取数据以及播放视频和音频文件等。在简要讨论运行时库的功能后,我们将介绍可用于构建 JavaFX 应用程序的开发工具,然后我们将研究打包和部署应用程序的可用选项。

JavaFX 平台

JavaFX 平台由编译器、一组运行时库和一些开发工具组成,其中包括用于 NetBeans 和 Eclipse 集成开发环境 (IDE) 的插件,使您能够在高效的开发环境中开发 JavaFX 应用程序。JavaFX 的一个优点是它运行在 Java 平台上,这意味着使用 JavaFX 编写的应用程序可以使用 Java 的所有安全和部署功能,并且除了 JavaFX 运行时本身提供的 API 外,还可以访问所有 Java 应用程序编程接口 (API)。图 1-1 显示了 JavaFX 平台的整体架构。

kt01_01.jpg

图 1-1 JavaFX 平台架构

JavaFX 应用程序使用 JavaFX Script 语言编写,这是本书第二部分的主题。JavaFX Script 的语法与 Java 足够接近,使 Java 开发人员易于学习,但又足够不同,使其学习成为一种有趣且有益的体验。本章稍后您将找到 JavaFX Script 的概述。

JavaFX 应用程序在 JavaFX 运行时的控制下运行。在撰写本文时,运行时有三个版本:一个用于桌面环境,运行在 Java SE 之上;另一个用于移动设备,运行在 Java ME 之上;第三个版本运行在 JavaTV 之上。当您下载 JavaFX 时,所有三个版本都会安装,并且当您在 NetBeans 或 Eclipse IDE 中编译和测试应用程序时,会自动选择正确的版本。

JavaFX 运行时将应用程序与底层 Java 平台的细节隔离开来,这意味着首次可以编写一个应用程序,几乎无需更改即可部署到多个环境中。换句话说,通过正确使用 JavaFX API,您可以编写一个可以在桌面、浏览器中(作为 Applet)、手机上(作为 MIDlet)或配备了适当设备的电视上运行的应用程序。为此,您不需要了解编写 Applet、MIDlet 或 Xlet 的细节。

为了实现这一点,JavaFX API 被分组为配置文件。配置文件由其提供的完整 JavaFX API 子集定义。在撰写本文时,有两个配置文件:

  • 通用配置文件,包含 API 中在所有受支持平台上可用且以相同方式工作的部分。这包括大部分用户界面类。
  • 桌面配置文件,包含依赖于 Java SE 平台存在的扩展。例如,反射是 Java SE 的一部分,但不是 Java ME 的一部分。

一个仅使用通用配置文件功能的 JavaFX 应用程序可以部署并在任何支持 JavaFX 的设备上执行,但一个利用桌面配置文件功能的应用程序只能在支持该特定配置文件的平台上运行。有些功能可以被任何应用程序使用,但在某些平台上不起作用。一个例子是效果 API,将在第 20 章“效果与混合”中讨论。可以使用 javafx.runtime.Platform 类(我们将在第 12 章“平台 API”中讨论)在运行时确定您的应用程序是否可以使用属于此类别的功能。

JavaFX 软件开发工具包 (SDK) 附带的 API 文档指示每个配置文件提供的包和类。本书涵盖通用和桌面配置文件,并明确指出不属于通用配置文件的功能。

JavaFX Script 语言

JavaFX Script 语言旨在让开发人员更容易编写 JavaFX 应用程序,特别是图形用户界面 (GUI) 应用程序。正如您在阅读本书第二部分时将看到的,JavaFX Script 具有多项针对 GUI 开发人员需求的功能。您还将发现 JavaFX 是一种比 Java 更“宽松”的语言。例如,以下是 JavaFX Script 中“Hello, World”的规范示例:

// "Hello, World" in JavaFX
println("Hello, World")

如您所见,只需要一行可执行代码。1 将其与 Java 等效代码进行比较:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}

在 Java 中,您必须创建一个类,定义一个具有特定签名的 main() 方法,然后编写要执行的代码。在 JavaFX 中,您可以跳过前两个部分,直接编写代码。当然,JavaFX 也有类,但您会发现它们不像在 Java 中那样突出。事实上,本书直到第 11 章“JavaFX Script 类”才提及用户定义的类。

变量声明

JavaFX 中的变量声明比 Java 更宽松。以下是您在 JavaFX 中声明和初始化字符串变量的方式:

var hello = "Hello, World";

变量声明需要关键字 var(或 def,如您在第 5 章“变量和数据类型”中将看到的),但您不必指定变量的类型——在这种情况下,编译器可以根据其初始化方式推断其类型必须是 String。此语言功能称为类型推断,将在第 5 章中更详细地讨论。在某些情况下,类型推断不起作用,但在最常见的情况下,此功能使您无需显式声明变量的类型。

尽管变量的类型不总是需要显式给出,但一旦确定,它就是固定的。这意味着,与 Java 一样,JavaFX 是一种静态类型语言,相比之下,JavaScript 支持动态类型变量,因此类型安全性较低。在 JavaFX 中,以下代码不合法:

var hello = "Hello, World";
hello = 123;  // Error!

由于其声明,变量 hello 的类型为 String,尽管代码中没有明确说明。该类型永久地与变量关联,因此为其分配任何非 String 值(例如本例中使用的数值 123)都是不合法的。在 JavaScript 和其他动态类型语言中,这种类型重赋值是合法的。

访问 Java API

尽管 JavaFX 有其自己的运行时库,但 JavaFX Script 应用程序也可以访问底层 Java 平台提供的 API。例如,以下代码使用 java.lang.Systemjava.util.Random 类来播种并打印一个随机整数:

package javafxintro;
 
import java.lang.System;
import java.util.Random;
 
var random = new Random(System.currentTimeMillis());
println(random.nextInt(10));

以这种方式访问 Java 类的能力使 JavaFX 能够利用底层平台的现有功能,例如网络或数据库访问(如果存在这些功能)。但是,依赖 Java API 可能会导致您的应用程序变得平台相关。例如,以下代码是有效的 JavaFX,但它并非完全可移植:

package javafxintro;
 
import java.lang.Math;
 
println(10 * Math.random());

此代码的问题在于,java.lang.Math 类仅在移动设备上部分实现——在基于移动信息设备配置文件 (MIDP) 的手机上(目前有数百万部正在使用中),random() 方法未实现。因此,如果您尝试为 JavaFX 移动配置文件构建此代码,它将无法编译。相比之下,上一个示例中使用的 Random 类确实在 Java ME 的 MIDP 配置文件中可用,因此在可能在手机上运行的 JavaFX 应用程序中使用它是安全的。2

对象字面量

JavaFX 中用于对象初始化的特殊语法,称为对象字面量,取代了 Java 中的 new 关键字。以下是一个对象字面量的示例:

Text {
    x: 20
    y: 20
    font: Font { size: 24 }
    content: "Hello, World"
}

此代码创建 javafx.scene.text.Text 类的一个实例,并初始化其 x、y、font 和 content 变量。编译器确保要分配给每个变量的值与其声明的类型兼容。对象字面量可以嵌套——在此示例中,一个嵌套的对象字面量用于使用 javafx.scene.text.Font 类的一个实例来初始化 font 变量,其中 size 变量的值已设置为 24。对象字面量将在第 5 章“变量和数据类型”以及第 6 章“表达式、函数和对象字面量”中讨论。

数据绑定。

编写 GUI 应用程序通常涉及使应用程序的不同部分状态相互同步。例如,您可能会构建一个包含输入字段和“确定”按钮的表单,并希望该按钮仅在输入字段不为空时才启用。在 Java 中,您通过编写一个侦听器来完成此操作,该侦听器在输入字段内容更改时调用,并根据字段内容启用或禁用按钮。在 JavaFX 中,您可以省去侦听器,直接将按钮的状态绑定到输入字段的内容,如下所示:

Button {
    text: "OK"
    disable: bind textBox.rawText.trim() == ""
}

假设 textBox 变量引用了平台独立的文本输入控件 TextBox 的实例(在第 22 章“跨平台控件”中讨论),表达式 textBox.rawText 获取输入到控件中的文本,使用 trim() 方法去除前导和尾随空格,并将结果与空字符串进行比较。3 此代码的结果是,如果 TextBox 中没有文本,按钮的 disable 变量将设置为 true(从而禁用它,因此忽略按它的尝试),如果存在文本则设置为 false。bind 关键字确保此链接得以维持,因此当 TextBox 的内容更改时,结果将重新计算,并且 disable 变量的值将自动更新。绑定可能是 JavaFX 最有用的功能,您将在本书的示例中非常频繁地看到它。

脚本和编译

JavaFX Script 由 JavaFX 编译器编译为类文件,然后由 Java 虚拟机 (JVM) 执行。脚本编译后,与编译后的 Java 源文件无异。编译器负责将每个 JavaFX Script 源文件映射到一个或多个类文件。大多数情况下,您不需要关心这是如何实现的,因为 JavaFX SDK 附带的工具隐藏了这些细节,如第 3 章“JavaFX Script 开发”和第 4 章“一个简单的 JavaFX 应用程序”中所述。您会看到映射的少数情况之一是需要分析未捕获异常的堆栈跟踪时。幸运的是,将堆栈跟踪中的堆栈帧映射到原始 JavaFX 代码通常很容易。

JavaFX Script 也可以通过使用 Java SE 6 内置的 Java Scripting API(也作为 Java 5 的单独下载提供)进行即时编译和执行。如果您需要从远程服务器下载脚本并在用户的平台上执行,这可能会很有用。只有在 Java VM(执行您的应用程序的那个)同时拥有 JavaFX 编译器和 Java Scripting API 的类时才能这样做,这意味着在仅提供通用配置文件的平台上无法实现这一点。

JavaFX 运行时

JavaFX 运行时可以分为两部分——一部分是与配置文件无关的运行时,应用程序和 JavaFX 编译器本身都使用它,另一部分是与配置文件相关的运行时,编译器对其没有依赖关系。一些与配置文件无关的运行时直接只被编译器和运行时库使用,但有许多公共 API 您的 JavaFX 应用程序可以使用,包括写入标准输出流的 println() 函数、让您读取命令行参数、Applet 参数和系统属性的函数,以及一个让您以与设备无关的方式存储信息的 API。这些有用的功能中的许多都在第 12 章“平台 API”中介绍。

作为通用配置文件一部分的 API 出现在所有运行时库的实现中。这三个现有运行时中实现这些 API 的代码可能不尽相同,但 API 本身是相同的,并且它们功能等效。JavaFX Desktop 的运行时还包含桌面配置文件特有 API 的实现。

运行时库是本书第三和第四部分的主题。在这里,我们简要介绍一下它们提供的一些最重要的功能。

用户界面类

运行时库的大部分由与创建用户界面相关的类组成。在 JavaFX 中,用户界面是节点的集合。节点可以表示某种形状,例如圆形或矩形,一些文本,或者更复杂的东西,例如视频或音频内容的播放器。节点可以放置在场景中,场景表示一个完整的用户界面,或者放置在中,然后将组放置在场景或另一个组中。提供了允许您移动、旋转、缩放或倾斜单个节点的操作。组中的节点可以作为一个独立的单元对待,可以对它应用任何这些操作。例如,对组应用旋转会导致整个组旋转。

图 1-2 展示了一个简单的 JavaFX 应用程序,它由多个节点组成,运行在 Windows PC 上。背景图像是一个节点,每个代表雪花的白色圆圈也是一个节点。应用程序底部统计可见雪花数量的文本也是一个节点。

kt01_02.jpg

图 1-2 一个简单的 JavaFX 应用程序

此应用程序仅使用通用配置文件中的 API,因此可以在任何支持 JavaFX 的手机上运行,无需更改。图 1-3 显示了同一个应用程序在移动设备模拟器上运行。您将在第 4 章中看到此示例的实现方式。

kt01_03.jpg

图 1-3 在移动设备模拟器上运行的 JavaFX 应用程序

视频和音频

JavaFX 支持在桌面和移动设备上播放视频和音频流。播放音频或视频只是创建一个名为 MediaView 的特定类型的节点,将其放置在场景中,然后让 JavaFX 运行时为您完成其余工作。

图 1-4 显示了在运行 JavaFX Desktop 平台的 PC 上播放视频的示例。

kt01_04.jpg

图 1-4 JavaFX 中的视频播放

一般而言,您可以播放主机平台支持的任何类型的音频或视频。这意味着,例如,Windows 平台上的 JavaFX 可以播放 Windows Media Player 可以播放的任何内容。此外,一些跨平台格式可以在任何可以运行 JavaFX 的设备上播放。有关更多信息,请参阅第 19 章“视频和音频”中对 JavaFX 视频和音频功能的详细讨论。

动画

JavaFX 在语言和运行时中都内置了对动画的支持。动画指的是在指定时间段内改变一个或多个变量值的能力。如果这些变量代表一个对象的位置,结果将是该对象看起来在移动。另一方面,如果您要改变一个对象的不透明度,那么该对象将随着时间的推移而逐渐出现或消失。

以下代码利用编译器和运行时支持来创建并播放一个持续 10 秒的动画。

1      var t = Timeline {
2          keyFrames: [
3              at (0s) { node.translateX => 0 }
4              at (10s) { node.translateX => 200 }
5          ]
6      }
7      t.play();

第 1 行使用的 Timeline 类是 JavaFX 运行时通用配置文件的一部分。第 3 行的代码表示,在经过 0 秒后,node 变量引用的节点的 translateX 变量(此代码片段中未显示)应设置为 0。第 4 行的代码表示,当 10 秒过去后,该节点的 translateX 变量应为 200。在这两个时间之间,该变量将具有由平台计算的中间值,例如,2 秒后值可能约为 40,4 秒后可能为 80,依此类推。4 translateX 变量控制节点的水平位置,因此此代码的效果将是节点将以均匀速度在屏幕上水平移动,在 10 秒内移动 200 像素。

语法 at (10s) 和符号 => 被编译器转换为一些更复杂的代码,这些代码创建一个 KeyFrame 对象并初始化它的一些变量。您将在第 4 章“一个简单的 JavaFX 应用程序”和第 18 章“动画”中找到所有详细信息和更多动画示例。

网络访问

由于 JavaFX 旨在用于编写富互联网应用程序,因此人们会期望它对通过互联网检索数据提供一些支持。虽然可以使用底层 Java 平台提供的网络支持,但这存在两个问题:

  • Java SE 和 Java ME 中的网络 API 彼此差异很大,这使得在不编写平台相关的粘合层来封装不同的网络 API 的情况下,无法编写平台独立且支持网络的应用程序。
  • 通过互联网访问数据是一个缓慢的过程,绝不能在用于管理用户界面的线程中执行。与 Java 不同,JavaFX 没有语言级别的并发支持——没有 synchronized 关键字的等效项。事实上,在 JavaFX 中,不鼓励应用程序代码管理线程。

为了解决这两个问题,JavaFX 运行时提供了包装常见网络操作并在内部管理的后台线程中处理它们的类。其目的是让应用程序指定需要做什么,然后让运行时负责异步处理细节。当任务完成时,应用程序代码将通过主线程中的回调得到通知。通过这种方式,所有并发问题都可以由 JavaFX 运行时以适合主机平台的方式进行管理。例如,运行时提供对调用基于 HTTP 的服务(例如 RESTful Web Service)的支持,并异步通知操作完成,以及在接收数据时提供进度报告。您将在第 27 章“使用外部数据源”中找到这些 API 的详细信息。

JavaFX 开发工具

JavaFX SDK 提供了一套可用于构建和打包 JavaFX 应用程序的工具。它包含以下内容:

  • 一个基于 Java 编译器并具有相同命令行参数的命令行编译器。您可以使用此编译器手动编译应用程序,或将其作为批处理脚本的一部分来构建整个项目。一个 JavaFX 特定选项允许您指定是为桌面、移动还是电视版本的 JavaFX 进行编译。
  • 一个 Ant 任务,您可以使用它通过 Ant 构建文件编译 JavaFX 源文件。该任务只是运行命令行编译器,将从构建文件获取的参数传递给它。
  • 一个运行 JavaFX 应用程序的应用程序启动器。有一个选项可以确定应用程序是应在桌面、移动还是电视模式下启动。
  • 一个名为 javafxpackager 的命令行实用程序,用于构建完整的 JavaFX 应用程序并将其打包以便交付到桌面、移动或电视平台。
  • 一个名为 javafxdoc 的命令行实用程序,它从一组 JavaFX 源文件中提取文档注释,并将其转换为一组 HTML 文档,其方式类似于 javadoc 实用程序。

除了命令行工具之外,NetBeans 和 Eclipse IDE 还提供了插件,使您可以在不离开开发环境的情况下编译、打包、运行和调试 JavaFX 应用程序。本书假定您将使用这些插件之一来运行示例代码,并且在第 4 章中创建简单但完整的 JavaFX 应用程序时,您将看到如何使用它们。

可以使用 Adobe Photoshop 和 Adobe Illustrator 等商业工具创建图形资源,然后将其导入 JavaFX 应用程序。JavaFX Production Suite 是 JavaFX SDK 的一个单独下载,它提供了插件,可以从 Photoshop 和 Illustrator 中导出 JavaFX 运行时能够理解的图形格式。您还可以使用 SVG(可缩放矢量图形)编辑器(如 Inkscape)准备图稿。JavaFX Production Suite 提供了一个实用程序,使您能够查看 SVG 文件并将其转换为 JavaFX 应用程序可以读取和使用的格式。这些工具将在第 21 章“导入图形”中讨论。

部署

JavaFX 应用程序目前可以通过四种不同的方式打包和部署以执行:

  • 作为使用 Java Web Start 交付和安装的桌面应用程序
  • 作为由 Web 浏览器交付并在 Java 插件中执行的 applet
  • 作为支持 JavaFX TV 配置文件的设备的电视应用程序
  • 作为交付到手机或其他移动设备的移动应用程序

这些情况下的打包细节由 javafxpackager 实用程序(我们将在第 27 章中讨论)或您的 IDE 处理。但是,这里有一些一般性要点值得注意。

Java 平台依赖项和安装

每个版本的 JavaFX 平台都依赖于它所设计的 JRE(Java 运行时环境)类型。对于 JavaFX Desktop,最低要求是 Java SE 5,尽管某些功能(例如透明度)只有在安装 Java SE 6 update 10 或更高版本时才启用。对于 JavaFX Mobile,最低要求是支持 Connected Limited Device Configuration (CLDC) 1.1、Mobile Information Device Profile (MIDP) 2.0 和 Mobile Services Architecture (MSA) 规范的设备,以及合适的 JavaFX 运行时。有关详细信息,请参阅您打算使用的 JavaFX 平台版本的发行说明。

长期以来,将 Java 作为桌面技术使用的一个问题是用户需要下载并安装适当版本的 Java 平台。尽管大多数 PC 和笔记本电脑现在都预装了 Java SE 版本,但用户可能仍然需要升级到更新的 JRE 才能运行 JavaFX 应用程序。在 Java SE 6 update 10 之前,安装或升级 JRE 意味着要等待一个非常大的下载完成。相比之下,使用 Adobe Flash、Flex 或 Microsoft Silverlight 编写的应用程序需要小得多的下载量,因此启动速度要快得多。从 update 10 开始,Java SE 6 包含一个称为消费者 JRE 的功能。消费者 JRE 将 Java 运行时拆分为一个最初下载的小内核,并且只有在需要时才获取和安装各种可选部分。其结果是,需要全新或升级安装 JRE 的 Java 应用程序、applet 和 JavaFX 应用程序现在将比使用早期版本的 JRE 启动得快得多。

Java 插件

最初的 Java 插件负责在 Web 浏览器的执行环境中托管小程序,是导致 Java 小程序不如其应有流行度的许多问题的原因,并导致 Flash、Flex 和 Silverlight 等替代技术的兴起,其中包括以下问题:

  • 加载 applet 的初始阶段在浏览器的主线程中执行,这会导致每当访问包含 Java applet 的网页时,浏览器都会冻结一段时间,无论用户是否真的想使用该 applet。
  • 在一个浏览器实例中只能执行一个插件实例,并且该插件实例在单个 Java VM 中运行所有 applet。当需要同时运行需要不同 JRE 版本的 applet 时,这会导致问题。
  • Applet 无法更改某些 Java VM 设置,例如最大堆大小。这可能会导致 Applet 作者无法充分控制 Applet 的执行环境以保证正确操作。

随着 Java SE 6 update 10 版本中引入新的 Java 插件,所有这些问题都得到了解决。安装了此版本 JRE 的用户在使用 JavaFX 和 Java applet 方面将比使用早期版本的用户获得更好的体验,并且由于 Java SE 7 中正在对 Java 平台进行模块化方面的额外工作,可能会有进一步的改进。

将小程序转换为已安装的应用程序

新 Java 插件的一个有趣功能是能够将小程序转换为已安装的应用程序,JavaFX 应用程序可以利用此功能。其目的是允许用户首先在 Web 浏览器中以小程序的形式运行您的应用程序,从而查看和体验它。在此模式下,将来使用应用程序需要用户返回到托管网页,以便重新加载小程序。

新的 Java 插件允许用户通过将 applet 从网页拖放到桌面来立即将其安装为应用程序。完成此操作后,该应用程序将独立于网络浏览器,并且可以直接从用户的桌面启动。您将在第 4 章中学习如何使用这个巧妙的小功能。

  1. 您可能已经注意到,这行代码的末尾没有分号。分号仅用于分隔两个 JavaFX 语句,因此在这种情况下我们可以省略它。您也不需要在函数中的最后一个语句之后,或者更一般地说,在块中的最后一个语句之后加上分号。当然,如果您的 Java 本能导致您反射性地在这里添加一个分号,那也没关系——编译器不会发出警告或错误。这是更宽松的语法规则的一个例子,这些规则使 JavaFX 比 Java 更容易使用。

  2. 在这种情况下,还有另一种选择——JavaFX 运行时包含 javafx.util.Math 类,其中也包含 random() 函数。此函数是通用配置文件的一部分,因此在所有 JavaFX 平台上都可用。

  3. 请注意,我们使用 == 运算符将 rawText 变量的值与空字符串进行比较。在 Java 中,我们必须使用 equals() 方法进行此比较,但 JavaFX 允许我们使用 == 代替,因为 == 将被编译器转换为 equals() 的调用,正如您将在本书后面看到的那样。这是 JavaFX 比 Java 更具程序员友好性的众多方式之一。

  4. 计算动画中间值的过程称为插值,由插值器执行。JavaFX 运行时包含一些标准插值器,您也可以编写自己的插值器。有关此主题的更多信息,请参阅第 18 章。

© . All rights reserved.