JavaFX-基础知识-全-

JavaFX 基础知识(全)

原文:zh.annas-archive.org/md5/E51DD19915A0979B8B23880AAD773381

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

正如其标题(JavaFX 8 Essentials)所暗示的,本书是一本实用的书,为您提供了一套强大的基本技能,将指导您自信地快速构建高性能的 JavaFX 8 客户端应用程序。这些应用程序利用现代 GPU 通过硬件加速图形,同时为您的客户提供引人注目、复杂和花哨的富客户端 GUI,这将给他们留下深刻的印象。

学习 JavaFX 8 基础知识是跳入创建应用程序的第一步,最重要的是它可以在任何平台上运行,从桌面、Web、移动设备、平板电脑到 Arduino、Raspberry Pi 和多核开发等嵌入式设备。遵循 Java 的“一次编写,到处运行”的范例,JavaFX 也保持了相同的特性。因为 JavaFX 8 完全是用 Java 语言从头开始编写的,所以您会感到非常熟悉。

大多数章节都是一个快节奏的指南,将帮助您快速入门 Java GUI 编程,利用 JavaFX 8 并在任何平台上部署和运行。

在阅读本书示例时,您会发现代码是使用 Java 8 上的 JavaFX 8 编写的(是的,Java SE 8),因此新的 API 和语言增强将帮助您成为更有生产力的开发人员。话虽如此,探索所有新的 Java 8 功能将非常方便(我鼓励您这样做)。

最后但同样重要的是,您将能够使用 JavaFX 开发令人惊叹的无触摸交互式运动应用程序,这些应用程序与 Leap 运动设备进行交互。

本书涵盖的内容

第一章,“使用 JavaFX 8 入门”,是对 JavaFX 8 的介绍。它讨论了 JavaFX 8 作为一种技术,为什么您应该关注它,它的历史,核心特性以及它可以使用的地方。

因此,现在是时候准备好正确的工具,并通过必要的步骤安装 JavaFX 8 及其支持的开发工具。在本章中了解将增加读者生产力的其他工具。作为我们正在正确的轨道上的最终验证,我们将以一个简单的 Hello JavaFX 应用程序结束本章。

第二章,“JavaFX 8 基础知识和创建自定义 UI”,讨论了没有比接收复杂的建议更令人沮丧的事情了。因此,我一直把重点放在基本要点上。为了在 JavaFX 场景上呈现图形,您将需要一个基本应用程序、场景、画布、形状、文本、控件和颜色。

此外,您将了解 JavaFX 8 基本应用程序结构,这些结构是未来任何应用程序的支柱。最后,我们还将探讨一些 Java SE 8 功能(如 Lambda、Streams、JavaFX 属性等),以帮助提高代码的可读性、质量和生产力。

在获得创建结构化 JavaFX 8 应用程序的实际经验后,如果您可以在不改变其功能的情况下更改应用程序的 UI,那将是件好事吗?在本章中,您将学习主题化以及如何通过应用各种主题(外观和感觉)和 JavaFX CSS 样式的基础知识来自定义应用程序。

您将使用 Scene Builder 以图形方式创建和定义 UI 屏幕,并将它们保存为 JavaFX FXML 格式的文件。最后,您将学习如何创建自定义控件。

第三章,“开发 JavaFX 桌面和 Web 应用程序”,涵盖了如何开发引人注目的桌面和 Web 应用程序,利用多核硬件加速的 GPU,提供高性能的基于 UI 的应用程序,并具有令人惊叹的外观。

由于 JavaFX 完全是用 Java 从头开始编写的,一些 Java SE 8 内置核心库将用于支持我们的应用程序。此外,您将学习如何将应用程序打包为独立应用程序以进行启动和分发。

此外,我们还将介绍任何 Web 应用程序中的基本核心 Web API,这些 API 由 JavaFX 8 支持,如javafx.scene.web.WebEnginejavafx.scene.web.WebView

我们还将讨论 JavaFX 与 HTML5 之间的关系,这很重要,因为它们互补。JavaFX 的丰富客户端 API,加上 HTML5 的丰富 Web 内容,创建了一种类似 RIA Web 应用程序的用户体验,具有本机桌面软件的特征。

第四章,“为 Android 开发 JavaFX 应用程序”,随着非 PC 客户端的增加,移动电话和平板电脑正在获得市场份额。JavaFX 8 可以为 Web 和桌面提供丰富的客户端应用程序。如果您编写 JavaFX 应用程序,请确保您希望它在尽可能多的设备上运行。本章将为您提供关于允许用户为 Android 手机创建本机应用程序的 SDK 的基本实践经验和知识。

第五章,“为 iOS 开发 JavaFX 应用程序”,是对上一章的延伸。如果您为 Android 编写 JavaFX 应用程序,请确保您希望它在尽可能多的 iOS 设备上运行。本章将为您提供关于允许用户为 Apple iOS 创建本机应用程序的 SDK 的基本实践经验和知识。

第六章,“在树莓派上运行 JavaFX 应用程序”,将为您提供开发在信用卡大小的计算机——树莓派板上运行的 JavaFX 8 应用程序所需的所有必要技能和知识。随着物联网(IoT)最近成为热门话题。Java 实际上是为物联网而生的。

第七章,“使用 JavaFX 监控和控制 Arduino”,涵盖了另一种物联网(IoT)。Arduino 是一个开源的电子原型平台,提供低成本的原型平台,支持自助概念和创客运动。

本章将为您提供所有必要的技能和知识,以快速使用 JavaFX 与 Arduino 板开发用于监控来自现实世界的数据或控制真实设备的桌面应用程序。

第八章,“使用 JavaFX 交互式 Leap Motion 应用程序”,将使您了解手势识别。您将发现一个令人惊叹的小工具——Leap Motion 设备,它将允许一种无触摸的方法来开发增强的 JavaFX 应用程序。

机器用户输入界面越来越不再以鼠标为中心,而是更倾向于多点触摸甚至无触摸输入。手势是人类如今可以自然地与机器交流的一种方式。

附录 A,“成为 JavaFX 大师”,将帮助您找到许多有用的链接和参考资料,帮助您进一步了解所有关于 JavaFX 的事情。

在本章结束时,请务必查看今天在生产中使用 JavaFX 的许多框架、库和项目。

本书所需内容

本书中给出的示例利用了写作时的最新 Java SE 8 版本,即 Java SE 8 更新 45 JDK 版本。从 Java SE 8 开始,它预先捆绑了我们在整本书中使用的 JavaFX 8。此外,NetBeans IDE 8.0.2 用作集成开发环境,以及 JavaFX 设计工具 Gluon Scene Builder 版本 8,作为一般软件和工具。

由于每个章节在其性质上都是独特的,并且需要特定的软件和硬件才能正常运行 JavaFX 8 示例,因此本书提供了所有必需的软件、工具和硬件,并详细解释了如何安装和配置它们,以便顺利运行 JavaFX 8 示例。

这本书适合谁

如果您是 Java 开发人员,有经验的 Java Swing、Flash/Flex、SWT 或 Web 开发人员,希望将客户端应用程序提升到更高水平,那么这本书适合您。这本书将帮助您开始创建一个时尚、可定制和引人入胜的用户界面。

此外,您还将学习如何快速创建高性能的富客户端应用程序,这些应用程序可以在任何平台上运行,无论是桌面、网络、移动还是嵌入式系统,比如树莓派、Arduino 以及基于无触控 Leap Motion 的应用程序。

这本书是一本快节奏的指南,将帮助您快速入门 Java GUI 编程,利用 JavaFX 8,在任何平台上部署和运行。

约定

在本书中,您会发现一些区分不同信息类型的文本样式。以下是一些这些样式的例子,以及它们的含义解释。

文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:“我们可以通过使用include指令包含其他上下文。”

代码块设置如下:

btn.setOnAction(new EventHandler<ActionEvent>() {
  @Override
  public void handle(ActionEvent event) {
    message.setText("Hello World! JavaFX style :)");
  }
});

任何命令行输入或输出都以以下形式书写:

$ gradle build

新术语重要单词以粗体显示。您在屏幕上看到的单词,比如菜单或对话框中的单词,会以这样的形式出现在文本中:“点击安装开始安装”。

注意

警告或重要说明会出现在这样的框中。

提示

提示和技巧会出现在这样。

读者反馈

我们始终欢迎读者的反馈。请告诉我们您对这本书的看法——您喜欢或不喜欢的地方。读者的反馈对我们开发能让您真正受益的标题至关重要。

要向我们发送一般反馈,只需发送电子邮件至<feedback@packtpub.com>,并在您的消息主题中提及书名。

如果您在某个专题上有专业知识,并且有兴趣撰写或为一本书做出贡献,请参阅我们的作者指南,网址为www.packtpub.com/authors

客户支持

现在您是 Packt 图书的自豪所有者,我们有一些事情可以帮助您充分利用您的购买。

下载示例代码

您可以从您在www.packtpub.com的帐户中下载您购买的所有 Packt 图书的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便直接将文件发送到您的电子邮件。

下载本书的彩色图片

我们还为您提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图片。彩色图片将帮助您更好地理解输出的变化。您可以从www.packtpub.com/sites/default/files/downloads/8026OS_ColorImages.pdf下载此文件。

勘误

尽管我们已经尽最大努力确保内容的准确性,但错误是难免的。如果您在我们的书籍中发现错误,无论是文字还是代码方面的错误,我们将不胜感激地接受您的报告。通过这样做,您可以帮助其他读者避免挫折,并帮助我们改进后续版本的书籍。如果您发现任何勘误,请访问www.packtpub.com/submit-errata进行报告,选择您的书籍,点击勘误提交表格链接,并输入您的勘误详情。一旦您的勘误经过验证,您的提交将被接受,并且勘误将被上传到我们的网站上,或者添加到该书籍的勘误列表中的 Errata 部分。您可以通过访问www.packtpub.com/support来查看任何现有的勘误。

盗版

互联网上侵犯版权的行为是跨媒体持续存在的问题。在 Packt,我们非常重视版权和许可的保护。如果您在互联网上发现我们作品的任何非法副本,请立即向我们提供地址或网站名称,以便我们采取补救措施。

如果您发现涉嫌盗版的材料,请通过<copyright@packtpub.com>与我们联系。

我们感谢您在保护我们的作者和我们为您提供有价值的内容的能力方面的帮助。

问题

如果您在阅读本书的过程中遇到任何问题,请通过<questions@packtpub.com>与我们联系,我们将尽力解决。

第一章:使用 JavaFX 8 入门

JavaFX 是 Java 的下一代图形用户界面(GUI)工具包。它是一个平台,可以轻松快速地构建高性能的 Java 客户端应用程序。

JavaFX 的底层引擎利用现代 GPU 通过硬件加速图形,同时提供设计良好的编程接口,从而使开发人员能够结合图形、动画和 UI 控件。

这些功能使您能够为客户提供引人入胜、复杂且完全可定制的客户端 GUI,这将让他们非常印象深刻。

虽然 Java 最初的目标是嵌入式和客户端世界,但自 2006 年以来,许多原因推动 Java 语言成为企业世界的顶级开发平台。

但最近,随着 JavaFX 平台作为标准客户端 GUI 的进入,这些最初的目标又开始重新流行起来。

尽管 JavaFX 不仅仅是一个 GUI 工具包,但它允许 Java 开发人员创建具有引人入胜的用户界面并轻松连接到后端系统的客户端应用程序。

此外,JavaFX 灵活的 FXML 支持使您能够轻松构建 MVC(模型-视图-控制器)架构模式应用,并使用 Scene Builder 工具采用所见即所得的方法。

JavaFX 的绑定功能简化了实体之间的通信,并进一步支持 MVC。除此之外,JavaFX 还提供了使用 CSS 快速、可定制的 UI 建模。

通过添加一个完整的WebView组件和文档模型,将其映射到 Java 代码变得容易,并为 3D 和媒体功能提供了很好的支持。

在本章中,我们将涵盖以下主题:

  • 什么是 JavaFX 以及它的目标平台?

  • JavaFX 历史概览

  • JavaFX 的目标、特性以及 JavaFX 8 的新功能

  • 如何安装 Java SE 8、JavaFX 8、NetBeans,并配置环境变量

  • 开发一个“Hello World”JavaFX 8 应用程序,并了解 JavaFX 8 的基本应用程序架构和构建模块

JavaFX 的目标

JavaFX 诞生的初衷是被用于许多类型的设备,如嵌入式设备、智能手机、电视、平板电脑和台式电脑。JavaFX 也遵循 Java 的“一次编写,到处运行”的范式。

JavaFX 8 完全使用 Java 语言编写,让您感到宾至如归。因此,使用 JavaFX 编写的应用程序可以部署在台式机、笔记本电脑、Web、嵌入式系统、移动设备和平板电脑上。

嵌入式系统不再得到 Oracle 的支持;它留给了像 ARM 等公司来支持。从 JavaFX 2.x 到 8.x,移动设备从未得到支持;现在的支持仅存在于 OpenJFX。社区受益于开源将 JavaFX 带入移动环境。

有关 OpenJFX 的更多信息,请访问wiki.openjdk.java.net/display/OpenJFX/Main

JavaFX 是一套图形和媒体包,使开发人员能够设计、创建、测试、调试和部署在各种平台上一致运行的丰富客户端应用程序,而无需使用许多单独的库、框架和 API 来实现相同的目标。这些单独的库包括媒体、UI 控件、WebView、3D 和 2D API。

因此,如果您是一名 Java 前端开发人员,一名有经验的 Java Swing、Flash/Flex、SWT 或 Web 开发人员,希望将您的客户端应用程序提升到一个新水平,并且想要为您的客户开发一个引人注目且复杂的用户界面,那么学习 JavaFX 技能是正确的选择——这本书适合您。

入门

本章是对 JavaFX 8 的介绍;我们已经谈到了 JavaFX 8 作为一种技术以及为什么你应该关心它。

接下来,我们将浏览其历史,探索其核心特性以及它可以使用的地方。

在开始使用本书学习 JavaFX 8 之前,我们将通过安装各种所需的软件捆绑包来准备您的开发环境,以便能够编译和运行其中的许多示例。

在本章中,您将学习如何安装所需的软件,如Java 开发工具包 JDK和 NetBeans 集成开发环境IDE)。

安装所需的软件后,您将首先创建一个传统的Hello JavaFX 8示例。一旦您对开发环境感到满意,作为我们正在正确的轨道上的最终验证,我们将浏览 Hello JavaFX 8 源代码,以了解基本的 JavaFX 8 应用程序架构。

如果您已经熟悉了 JDK 和 NetBeans IDE 的安装,您可以跳转到第二章,JavaFX 8 基础知识和创建自定义 UI,其中涵盖了 JavaFX 8 的基础知识以及如何创建自定义 UI 组件。

那么你还在等什么?让我们开始吧!

JavaFX 历史

你可能认为 JavaFX 是一种相当新的技术,但实际上并不是。JavaFX 已经存在很长时间了;自 2005 年以来就一直存在。自从 Sun Microsystems 收购了SeeBeyond公司以来,就有了一个名为F3Form Follows Function)的图形丰富的脚本语言,由工程师 Chris Oliver 创建。

在 2007 年的 JavaOne 大会上,Sun Microsystems 正式将 JavaFX 作为语言的名称公布,而不是 F3。在 2007 年至 2010 年期间,甲骨文收购了许多大公司,如 BEA Systems、JD Edwards、Siebel Systems 等。当时我正在甲骨文工作,负责将不同的客户支持渠道整合到甲骨文支持网站MetaLink中。

2009 年 4 月 20 日,甲骨文公司宣布收购 Sun Microsystems,使甲骨文成为 JavaFX 的新管理者。

在 2010 年的 JavaOne 大会上,甲骨文宣布了 JavaFX 路线图,其中包括计划淘汰 JavaFX 1.3 脚本语言并为基于 Java 的 API 重新创建 JavaFX 平台。如承诺的那样,JavaFX 2.0 SDK 于 2011 年 10 月的 JavaOne 上发布。

除了发布 JavaFX 2.0 之外,Oracle 还通过宣布致力于采取措施使 JavaFX 开源,从而允许 Java 多才多艺和强大的社区帮助推动平台发展。JavaFX 开源增加了其采用率,使得错误修复的周转时间更快,并产生了新的增强功能。

在 JavaFX 2.1 和 2.2 之间,新功能的数量迅速增长。JavaFX 2.1 是 Java SDK 在 Mac OS 上的官方发布。JavaFX 2.2 是 Java SDK 在 Linux 操作系统上的官方发布。

没有 JavaFX 3.x 这样的东西,但是在 2014 年 3 月 18 日宣布的 Java SE 8 发布中,Java 开发世界发生了重大变化。Java SE 8 具有许多新的 API 和语言增强功能,包括Lambda、Stream API、Nashorn JavaScript 引擎和JavaFX API,这些都被纳入标准 JDK 捆绑包中,JavaFX 版本成为直接继承 JavaFX 2.0 的 8。

要查看 Java SE 8 中的所有新功能,请访问www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html

JavaFX 8 何时可用?

答案是现在。如前所述,Java SE 8 于 2014 年 3 月 18 日发布。对于使用 Java 构建客户端应用程序的开发人员,JavaFX 丰富的互联网应用程序框架现在支持 Java 8。

大多数 Java 企业版供应商也支持 Java 8。是否立即转移到 Java SE 8 取决于您正在处理的项目类型。

事实上,根据 Oracle JDK 支持路线图,在 2015 年 4 月之后,Oracle 将不会在其公共下载站点上发布 Java SE 7 的进一步更新。

JavaFX API 作为Java SE Runtime EnvironmentJRE)和 JDK 的完全集成功能可用。JDK 适用于所有主要桌面平台(WindowsMac OS XSolarisLinux),因此 JavaFX 也将在所有主要桌面平台上运行。

关于 JavaFX 8,它支持以下 API:

  • 3D 图形

  • 富文本支持

  • 打印 API。

JavaFX 功能

根据 JavaFX 的官方文档,以下功能包括在 JavaFX 8 及以后的版本中:

  • Java API:JavaFX 是一个由 Java 代码编写的类和接口的 Java 库。

  • FXML 和 Scene Builder:这是一种基于 XML 的声明性标记语言,用于构建 JavaFX 应用程序用户界面。您可以在 FXML 中编码,也可以使用 JavaFX Scene Builder 交互式设计 GUI。Scene Builder 生成可以移植到像 NetBeans 这样的 IDE 中的 FXML 标记,您可以在其中添加业务逻辑。此外,生成的 FXML 文件可以直接在 JavaFX 应用程序中使用。

  • WebView:这是一个 Web 组件,使用WebKit,一种 HTML 渲染引擎技术,可以在 JavaFX 应用程序中嵌入网页。在WebView中运行的 JavaScript 可以调用 Java API,反之亦然。

  • Swing/SWT 互操作性:现有的 Swing 和 SWT 应用程序可以从 JavaFX 功能中受益,如丰富的图形、媒体播放和嵌入式网页内容。

  • 内置 UI 控件和 CSS:JavaFX 提供了所有主要的 UI 控件,以及一些额外的不常见的控件,如图表、分页和手风琴,这些控件是开发完整功能的应用程序所需的。组件可以使用标准的 Web 技术(如 CSS)进行皮肤化。

  • 3D 图形功能:包括对 3D 图形库的支持。

  • Canvas API:您可以使用 Canvas API 直接在 JavaFX 场景区域内绘制,它由一个图形元素(节点)组成。

  • 多点触控支持:基于底层平台的能力支持多点触控操作。

  • 硬件加速图形管道:JavaFX 图形基于图形渲染管道Prism。当与支持的图形卡或图形处理单元GPU)一起使用时,Prism 引擎可以平滑快速地渲染 JavaFX 图形。如果系统不具备其中之一,则 Prism 将默认为软件渲染堆栈。

  • 高性能媒体引擎:该引擎提供了一个稳定的、低延迟的基于GStreamer多媒体框架的媒体框架。支持 Web 多媒体内容的播放。

  • 自包含部署模型:自包含应用程序包含所有应用程序资源和 Java 和 JavaFX 运行时的私有副本。它们被分发为本机可安装的软件包,并为该操作系统提供与本机应用程序相同的安装和启动体验。

JavaFX 8 的新功能

以下是 Java SE 8 版本的 JavaFX 组件中新增功能和重大产品变更的简要总结:

  • 新的Modena 主题现在是 JavaFX 应用程序的默认主题。

  • 增加了对其他 HTML5 功能的支持,包括 Web Sockets、Web Workers、Web Fonts 和打印功能。

  • 该 API 使您能够使用新的SwingNode类将Swing内容嵌入到 JavaFX 应用程序中,从而改进了 Swing 互操作性功能。

  • 现在提供了内置的 UI 控件DatePickerSpinnerTableView

  • 它通过javafx.print包提供了公共 JavaFX 打印 API。

  • 支持高 DPI 显示。

  • CSS 可样式化类成为公共 API。

  • 引入了一个计划服务类。

  • 3D 图形库已经增强了几个新的 API 类。

  • 在此版本中,Camera API类进行了重大更新。

  • 现在 JavaFX 8 支持丰富的文本功能。这些包括在 UI 控件中支持泰语和印地语等双向和复杂文本脚本,以及文本节点中的多行、多样式文本。

  • 对话框和辅助功能 API 得到支持。

在附录中,成为 JavaFX 专家,我提供了您成为 JavaFX 专家所需的所有参考资料(链接、书籍、杂志、文章、博客和工具)和真实的 JavaFX 8 生产应用程序的清单。

下图显示了使用 JavaFX 8 构建的Ensemble8.jar应用程序,展示了处理各种 JavaFX 8 组件、主题和概念的示例。更有趣的是,源代码可供学习和修改-请参阅最后一章以了解如何安装此应用程序。

JavaFX 8 的新功能

JavaFX 8 应用程序

应用程序涵盖了许多主题,特别是新的 JavaFX 8 3D API,可以在下图中的 3D 图形部分找到:

JavaFX 8 的新功能

JavaFX 8 3D 应用程序

安装所需的软件

到目前为止,我们已经对 JavaFX 有了一个很好的介绍,我和你一样急于开始创建和启动我们的第一个"Hello JavaFX 8"应用程序。但是,如果没有下载和安装允许我们创建和编译本书大部分代码的正确工具,这是不可能的。

您需要下载并安装Java 8 Java 开发工具包(JDK)或更高版本。而不是运行时版本(JRE)。

从以下位置下载最新的 Java SE 8u45 JDK 或更高版本:

www.oracle.com/technetwork/java/javase/downloads/index.html

从以下链接下载并安装 NetBeans 8.0.2 或更高版本netbeans.org/downloads,尽管推荐使用 NetBeans IDE All Bundle,您也可以使用 Java EE 捆绑包,如图所示:

安装所需的软件

NetBeans 捆绑包下载。

目前,JavaFX 8 可以在以下操作系统上运行:

  • Windows 操作系统(XP、Vista、7、8)32 位和 64 位

  • Mac OS X(64 位)

  • Linux(32 位和 64 位),Linux ARMv6/7 VFP,HardFP ABI(32 位)

  • Solaris(32 位和 64 位)

安装 Java SE 8 JDK

本节概述的步骤将指导您成功下载和安装 Java SE 8。从以下位置下载 Java SE 8 JDK:

www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

在以下步骤中,将以 Mac OS X Yosemite(10.10.3)操作系统上的 Java SE 8u45 JDK 64 位版本(写作时)为例。

其他操作系统和 JDK 版本的步骤类似。但是,如果您的环境不同,请参考以下链接获取更多详细信息:

docs.oracle.com/javase/8/docs/technotes/guides/install/toc.html

以下是安装 Java SE 8 JDK 的步骤:

  1. 通过启动图像文件jdk-8u45-macosx-x64.dmg来安装 Java 8 JDK。一旦启动了 JDK 8 设置图像文件,屏幕将出现如下截图。这是软件包设置文件。双击它,安装程序将启动:安装 Java SE 8 JDK

JDK 8 设置图像文件

提示

通常,您需要在计算机上拥有管理员权限才能安装软件。

  1. 开始设置 Java 8 JDK。在安装过程开始时,将出现以下屏幕截图中的屏幕。单击继续按钮,然后在安装类型屏幕向导上,单击安装开始安装。安装 Java SE 8 JDK

Java SE 开发工具包 8 设置

  1. 一旦点击安装,您可能会被要求输入密码。输入密码,单击确定,安装将继续进行,显示一个进度条,如下图所示:安装 Java SE 8 JDK

Java SE 开发工具包 8 安装进行中

  1. 设置将完成 Java 8 SE 开发工具包的安装。单击关闭按钮退出。

设置环境变量

现在您需要设置一些关键的环境变量。如何设置它们以及它们应该设置的值取决于您的操作系统。需要设置的两个变量是:

  • JAVA_HOME:这告诉您的操作系统 Java 安装目录在哪里。

  • PATH:这指定了 Java 可执行目录的位置。这个环境变量让系统搜索包含可执行文件的路径或目录。Java 可执行文件位于JAVA_HOME主目录下的 bin 目录中。

为了使JAVA_HOMEPATH更加永久,您将希望以这样的方式将它们添加到系统中,以便在每次启动或登录时都可以使用。根据您的操作系统,您需要能够编辑环境变量名称和值。

Windows 环境中,您可以使用键盘快捷键Windows 徽标键+暂停/中断键,然后单击高级系统设置以显示系统属性对话框。

接下来,单击环境变量。这是您可以添加、编辑和删除环境变量的地方。您将使用已安装的主目录作为值来添加或编辑JAVA_HOME环境变量。在 Windows 操作系统的环境变量对话框中显示的是这个屏幕截图:

设置环境变量

Windows 环境变量

让我们设置环境变量:

  • 要为Mac OS X平台设置JAVA_HOME环境变量,您需要启动终端窗口,编辑您的主目录的.bash_profile文件,添加以下导出命令:
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)

  • 在使用 Bash shell 环境的Linux和其他Unix操作系统上,启动终端窗口并编辑~/.bashrc~/.profile文件,包含导出命令:
export JAVA_HOME=/usr/java/jdk1.8.0
export PATH=$PATH:$JAVA_HOME/bin

  • 在使用C shell(csh)环境的 Linux 和其他 Unix 操作系统上,启动终端窗口并编辑~/.cshrc~/.login文件,包含setenv命令:
setenv JAVA_HOME /usr/java/jdk1.8.0_45
setenv PATH ${JAVA_HOME}/bin:${PATH}

设置好路径和JAVA_HOME环境变量后,您将希望通过启动终端窗口并从命令提示符执行以下两个命令来验证您的设置:

java -version
javac –version

注意

每种情况下的输出都应该显示一个消息,指示语言和运行时的 Java SE 8 版本。

安装 NetBeans IDE

在开发 JavaFX 应用程序时,您将使用 NetBeans IDE(或您喜欢的任何其他 IDE)。请确保下载包含 JavaFX 的正确 NetBeans 版本。要安装 NetBeans IDE,请按照以下步骤进行:

  1. 从以下位置下载 NetBeans IDE 8.0.2 或更高版本:

netbeans.org/downloads/index.html

  1. 启动.dmg镜像文件netbeans-8.0.2-macosx.dmg。镜像将被验证,打开一个包含安装程序包存档netbeans-8.0.2.pkg的文件夹;双击它以启动安装程序。将出现一个带有消息的对话框:此软件包将运行一个程序来确定是否可以安装软件。单击继续按钮。

  2. 一旦启动了 NetBeans 安装对话框,再次点击继续。接下来,接受许可证并点击继续,然后点击同意

  3. 点击安装按钮继续。下面的屏幕截图显示了一个Mac安全警告提示;输入密码并点击安装软件安装 NetBeans IDE

Mac 安全警告对话框

  1. NetBeans IDE 安装过程将开始。下面的屏幕截图显示了安装进度条:安装 NetBeans IDE

安装进度

  1. 点击关闭按钮完成安装,如下所示:安装 NetBeans IDE

设置完成

现在,您已经准备好继续创建 JavaFX 应用程序了。

创建“Hello World” JavaFX 风格的应用程序

展示创建和构建 JavaFX 应用程序的最佳方式是使用Hello World应用程序。

在本节中,您将使用刚刚安装的 NetBeans IDE 来开发、编译和运行基于 JavaFX 的Hello World应用程序。

使用 Netbeans IDE

要快速开始创建、编码、编译和运行一个简单的 JavaFX 风格的Hello World应用程序,使用 NetBeans IDE,按照本节中概述的步骤进行操作:

  1. 文件菜单中选择新建项目

  2. JavaFX 应用程序类别中选择JavaFX 应用程序。点击下一步

  3. 将项目命名为HelloJavaFX。可选地,您可以为应用程序类定义包结构。然后点击完成,如下所示:使用 Netbeans IDE

新的 JavaFX 应用程序向导

NetBeans 打开HelloJavaFX.java文件,并用基本的“Hello World”应用程序的代码填充它。

注意

您会发现,这个版本的代码与 NetBeans 实际创建的代码有些不同,您可以进行比较以找出差异,但它们具有相同的结构。我这样做是为了在单击Say 'Hello World'按钮时,将结果显示在Scene上的文本节点上,而不是控制台上。为此,还使用了VBox容器。

  1. 右键单击项目,然后从菜单中点击运行,如下所示:使用 Netbeans IDE

运行应用程序

  1. NetBeans 将编译和运行该应用程序。输出应该如下所示的屏幕截图:使用 Netbeans IDE

从 NetBeans IDE 启动的 JavaFX Hello World

  1. 点击按钮,您应该看到以下结果:使用 Netbeans IDE

JavaFX Hello World 结果

这是基本的 Hello world 应用程序(HelloJavaFX.java)的修改后的代码:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import static javafx.geometry.Pos.CENTER;
import javafx.scene.layout.VBox;

/**
  * @author mohamed_taman
 */
public class HelloJavaFX extends Application {

  @Override
  public void start(Stage primaryStage) {

    Button btn = new Button();
    Text message = new Text();

    btn.setText("Say 'Hello World'");

    btn.setOnAction(event -> {
      message.setText("Hello World! JavaFX style :)");
    });

    VBox root = new VBox(10,btn,message);
    root.setAlignment(CENTER);

    Scene scene = new Scene(root, 300, 250);

    primaryStage.setTitle("Hello JavaFX 8 World!");
    primaryStage.setScene(scene);
    primaryStage.show();
  }
  public static void main(String[] args) {
    launch(args);
  }
}

工作原理

以下是关于 JavaFX 应用程序基本结构的重要信息:

  • JavaFX 应用程序的主类应该扩展javafx.application.Application类。start()方法是所有 JavaFX 应用程序的主入口点

  • JavaFX 应用程序通过舞台场景定义用户界面容器。JavaFX Stage类是顶级 JavaFX 容器。JavaFX Scene类是所有内容的容器。以下代码片段创建了一个舞台和场景,并使场景在给定的像素大小下可见 - new Scene(root, 300, 250)

  • 在 JavaFX 中,场景的内容表示为节点的分层场景图。在本例中,根节点是一个VBox布局对象,它是一个可调整大小的布局节点。这意味着根节点的大小跟踪场景的大小,并且在用户调整舞台大小时发生变化。

  • 在这里,VBox被用作容器,以单列多行的形式垂直排列其内容节点。我们将按钮btn控件添加到列中的第一行,然后将文本message控件添加到同一列的第二行,垂直间距为 10 像素,如下面的代码片段所示:

VBox root = new VBox(10,btn,message);
root.setAlignment(CENTER);
  • 我们设置了带有文本的按钮控件,以及一个事件处理程序,当单击按钮时,将消息文本控件设置为Hello World! JavaFX style 😃

  • 您可能会注意到在 Java 中有一种奇怪的代码语法,没有编译器错误。这是一个Lambda表达式,它已经添加到了 Java SE 8 中,我们将在第二章 JavaFX 8 Essentials and Creating a custom UI中简要讨论它。与旧的匿名内部类风格相比,现在使用 Lambda 表达式更清晰、更简洁。看一下这段代码的比较:

老派:

btn.setOnAction(new EventHandler<ActionEvent>() {
  @Override
  public void handle(ActionEvent event) {
    message.setText("Hello World! JavaFX style :)");
  }
});

新时代:

btn.setOnAction(event -> {
    message.setText("Hello World! JavaFX style :)");
});
  • 当使用 JavaFX Packager 工具创建应用程序的JAR文件时,不需要main()方法,该工具会将 JavaFX Launcher 嵌入 JAR 文件中。

  • 然而,包括main()方法是有用的,这样您就可以运行没有 JavaFX Launcher 创建的 JAR 文件,比如在使用 JavaFX 工具没有完全集成的 IDE 中。此外,嵌入 JavaFX 代码的Swing应用程序需要main()方法。

  • 在我们的main()方法的入口点,我们通过简单地将命令行参数传递给Application.launch()方法来启动 JavaFX 应用程序。

  • Application.launch()方法执行后,应用程序将进入就绪状态,框架内部将调用start()方法开始执行。

  • 此时,程序执行发生在JavaFX 应用程序线程上,而不是在主线程上。当调用start()方法时,一个 JavaFX javafx.stage.Stage对象可供您使用和操作。

注意

高级主题将在接下来的章节中进行详细讨论。更重要的是,我们将在接下来的章节中深入讨论 JavaFX 应用程序线程。在最后三章中,我们将看到如何将其他线程的结果带入 JavaFX 应用程序线程,以便在场景中正确呈现它。

总结

到目前为止,您已经了解了 JavaFX 是什么,并见识了它的强大。您已经成功下载并安装了 Java 8 JDK 和 NetBeans IDE。在成功安装了先决条件软件之后,您通过 NetBeans IDE 创建了一个 JavaFX Hello World GUI 应用程序。在学习了如何编译和运行 JavaFX 应用程序之后,您快速浏览了源文件HelloJavaFX.java的代码。

接下来,在第二章 JavaFX 8 Essentials and Creating a custom中,您将了解 JavaFX 8 架构组件和引擎,这些组件和引擎使 JavaFX 应用程序在底层高效平稳地运行。您还将了解最常见的布局 UI 组件,并了解如何为整个应用程序或单个场景节点设置主题。

我们还将介绍 Java SE 8 最重要的特性,Lambda 表达式,以及它的工作原理。然后我们将深入了解Scene Builder作为一种声明式 UI 和高效工具,然后学习生成的基于 FXML 的标记文档以及如何将其导入到 NetBeans IDE 中,以继续将应用程序逻辑实现与已声明的 UI 控件关联起来。

最后,您将能够创建一个自定义的 UI 组件,该组件不与默认的 JavaFX 8 UI 控件捆绑在一起。

第二章:JavaFX 8 基础知识和创建自定义 UI

了解 JavaFX 的基本知识肯定会帮助您轻松构建复杂的 UI 解决方案。

在本章中,您将简要介绍 JavaFX 8 架构,以便了解 JavaFX 架构组件和引擎如何有效地相互连接,并使其图形平滑地渲染。

您将学习如何在 JavaFX 场景上呈现图形,并为此创建一个使用场景、一些控件和样式的基本应用程序。

我们将涉及 Java SE 8 功能的基础知识(如Lambda函数接口),以帮助提高代码的可读性、质量和生产力。

一旦我们有了第一个结构良好的 JavaFX 8 应用程序,如果您可以在不改变其功能的情况下更改应用程序的 UI,那不是很好吗?您将通过查看 JavaFX CSS 样式的基础知识来了解主题。

最后,您将了解如何使用 Scene Builder 以图形方式创建和定义 UI 屏幕,并将其保存为 JavaFX FXML 格式的文件。您还将亲身体验创建自定义控件

在本章中,我们将涵盖以下主题:

  • 了解 JavaFX 架构组件

  • 使用 JavaFX 组件设置 UI

  • 使用 Java SE 8,Lambda 表达式和其他功能

  • 为不同平台定制应用程序的主题

  • 使用 CSS 自定义应用程序 UI

  • 使用 Scene Builder 工具以可视化方式创建 UI

  • 使用 FXML 构建自定义 UI

JavaFX 8 架构的快速回顾

为了更好地理解框架的组件和引擎如何相互交互以运行您的 JavaFX 应用程序,本节对 JavaFX 架构和生态系统进行了高层次描述。

以下图示了 JavaFX 平台的架构组件。它显示了每个组件以及它们如何相互连接。

负责运行 JavaFX 应用程序代码的引擎位于 JavaFX 公共 API 的下方。

此引擎由子组件组成。这些包括Prism,一个 JavaFX 高性能图形引擎;Glass 工具包,一个小巧高效的窗口系统;媒体引擎;和 Web 引擎。

注意

虽然这些组件没有通过公共 API 公开,但我们将对它们进行描述,以便您更好地了解是什么使 JavaFX 应用以高效的方式成功运行。

JavaFX 8 架构的快速回顾

JavaFX 架构图

有关 JavaFX 架构和生态系统的更多信息,请访问docs.oracle.com/javase/8/javafx/get-started-tutorial/jfx-architecture.htm

场景图

每个应用程序都有一个起始根点来构建 UI 层次结构,而 JavaFX 应用程序的起始点是场景图。在前面的屏幕截图中,它显示为蓝色的顶层的一部分。它是表示应用程序用户界面的所有视觉元素的根节点树。它还跟踪和处理任何用户输入,并且可以被渲染,因为它本身是一个 UI 节点。

Node是场景图树中的任何单个元素。每个节点默认具有这些属性 - 用于标识的 ID,用于更改其视觉属性的样式类列表,以及用于正确适应场景并放置在其父布局容器节点内的边界体积,除了场景图的根节点。

场景图树中的每个节点都有一个父节点,但可以有零个或多个子节点;但是,场景根节点没有父节点(为空)。此外,JavaFX 具有一种机制,以确保节点只能有一个父节点;它还可以具有以下内容:

  • 视觉效果,如模糊和阴影

  • 通过不透明度控制组件的透明度

  • CPU 加速的 2D 变换、过渡和旋转

  • 3D 变换,如过渡、缩放和旋转

  • 事件处理程序(如鼠标事件、键盘事件或其他输入方法,如触摸事件)

  • 应用程序特定状态

下图显示了舞台、场景、UI 节点和图形树之间的关系:

场景图

JavaFX UI 树层次关系

图形原语也是 JavaFX 场景图的一个组成部分,如线条、矩形和文本,以及图像、媒体、UI 控件和布局容器。

当涉及为客户提供复杂和丰富的 UI 时,场景图简化了这项任务。此外,您可以使用javafx.animation API 快速轻松地为场景图中的各种图形添加动画。

除了这些功能外,javafx.scene API 还允许创建和指定几种内容类型,如下所示:

  • 节点:表示为 UI 控件、图表、组、容器、嵌入式 Web 浏览器、形状(2D 和 3D)、图像、媒体和文本的任何节点元素

  • 效果:这些是简单的对象,当应用于 UI 节点时,会改变其在场景图节点上的外观,如模糊、阴影和颜色调整

  • 状态:任何特定于应用程序的状态,如变换(节点的位置和方向)和视觉效果

JavaFX 功能的 Java 公共 API

这是作为一套完整的 Java 公共 API 的瑞士军刀工具包,支持丰富的客户端应用程序开发。

这些 API 为您提供了前所未有的灵活性,通过将 Java SE 平台的最佳功能与全面的沉浸式媒体功能相结合,构建直观而全面的一站式开发环境,用于构建丰富的客户端 UI 应用程序。

这些 JavaFX 的 Java API 允许您执行以下操作:

  • 利用 Java SE 的强大功能,从泛型、注解和多线程到新的 Lambda 表达式(Java SE 8 中引入)。

  • 为 Web 开发人员提供了一种更简单的方式,可以从其他基于 JVM 的动态语言(如JavaScript)中使用 JavaFX。

  • 通过集成其他系统语言(如Groovy)编写大型复杂的 JavaFX 应用程序。

  • 将 UI 控件绑定到控制器属性,以便从模型到绑定的 UI 节点自动通知和更新。绑定包括对高性能延迟绑定、绑定表达式、绑定序列表达式和部分绑定重新评估的支持。我们将在第三章中看到这一点以及更多内容,开发 JavaFX 桌面和 Web 应用程序

  • 引入可观察列表和映射,允许应用程序将 UI 连接到数据模型,观察这些数据模型的变化,并相应地更新相应的 UI 控件,通过扩展 Java 集合库。

图形系统

JavaFX 图形系统,如前图中的紫色所示,支持在 JavaFX 场景图层上平稳运行的 2D 和 3D 场景图。作为该层下面的实现细节,它在运行在没有足够图形硬件支持硬件加速渲染的系统时,提供了渲染软件堆栈。

JavaFX 平台有两个实现图形加速管道:

  • Prism:这是处理所有渲染作业的引擎。它可以在硬件和软件渲染器上运行,包括 3D。JavaFX 场景的光栅化和渲染由此引擎处理。根据使用的设备,可能存在以下多个渲染路径:

  • DirectX 9 在 Windows XP 和 Vista 上,DirectX 11 在 Windows 7 上

  • OpenGL 在 Linux、Mac 和嵌入式系统上

  • 当无法进行硬件加速时进行软件渲染。

  • Quantum Toolkit:这负责将 Prism 引擎和玻璃窗口工具包连接起来,使它们在堆栈中的 JavaFX 层中可用。这是除了管理与渲染与事件处理相关的任何线程规则。

玻璃窗口工具包

如上图中间部分所示,玻璃窗口工具包作为连接 JavaFX 平台与本机操作系统的平台相关层。

由于其主要责任是提供本机操作服务,例如管理定时器、窗口和表面,因此它在渲染堆栈中的位置最低。

JavaFX 线程

通常,系统在任何给定时间运行两个或更多以下线程:

  • JavaFX 应用程序线程:这是 JavaFX 应用程序使用的主要线程。

  • Prism 渲染线程:这将渲染与事件分发器分开处理。它在准备处理下一个 N + 1 帧时渲染 N 帧。它的最大优势是能够执行并发处理,特别是在具有多个处理器的现代系统上。

  • 媒体线程:这在后台运行,并通过 JavaFX 应用程序线程通过场景图同步最新帧。

  • Pulse:这使您能够以异步方式处理事件。它帮助您管理 JavaFX 场景图元素状态与 Prism 引擎场景图元素事件之间的同步。当它被触发时,场景图上元素的状态将与渲染层同步。

注意

任何布局节点和 CSS 也与脉冲事件相关联。

玻璃窗口工具包使用高分辨率本机定时器执行所有脉冲事件。

媒体和图像

JavaFX javafx.scene.media API 提供媒体功能。JavaFX 支持视觉和音频媒体。对于音频文件,它支持MP3AIFFWAV文件以及FLV视频文件。

您可以通过 JavaFX 媒体提供的三个主要独立组件访问媒体功能-Media对象表示媒体文件,MediaPlayer播放媒体文件,MediaView是一个将媒体显示到您的场景图中的节点。

注意

媒体引擎组件,如上图中橙色所示,经过精心设计,以稳定性和性能为考量,以在所有支持的平台上提供一致的行为。

Web 组件

如上图中绿色所示,Web 引擎组件是最重要的 JavaFX UI 控件之一,它基于 WebKit 引擎构建,这是一个支持 HTML5、JavaScript、CSS、DOM 渲染和 SVG 图形的开源 Web 浏览器引擎。它通过其 API 提供 Web 查看器和完整的浏览功能。在第三章中,开发 JavaFX 桌面和 Web 应用程序,我们将深入研究这一点,当开发 Web 应用程序时。

它允许您在 Java 应用程序中添加和实现以下功能:

  • 从本地或远程 URL 渲染任何 HTML 内容

  • 提供后退和前进导航,并支持历史

  • 重新加载任何更新的内容

  • 对 Web 组件进行动画处理和应用 CSS 效果

  • 为 HTML 内容提供丰富的编辑控件

  • 可以执行 JavaScript 命令并处理 Web 控件事件

布局组件

在构建丰富和复杂的 UI 时,我们需要一种方式来允许在 JavaFX 应用程序中的 UI 控件内进行灵活和动态的排列。这是使用布局容器或窗格的最佳位置。

布局 API 包括以下容器类,它们自动化常见的布局 UI 模式:

  • BorderPane:这将其内容节点布局在顶部、底部、右侧、左侧或中心区域

  • HBox:这将其内容节点水平排列在一行中

  • VBox:这将其内容节点垂直排列在单列中

  • StackPane:这将其内容节点放置在面板中心的前后单一堆栈中

  • GridPane:这使得可以创建一个灵活的行和列网格,用于布置内容节点

  • FlowPane:这将其内容节点以水平或垂直流的方式排列,在指定的宽度(水平)或高度(垂直)边界处换行

  • TilePane:这将其内容节点放置在统一大小的布局单元或瓷砖中

  • AnchorPane:这使得可以将锚节点创建到布局的顶部、底部、左侧或中心,并且可以自由定位其子节点

提示

在 JavaFX 应用程序中可以嵌套不同的容器;为了实现所需的布局结构,我们将在下一步中看到这一点,当开发我们的自定义 UI 时。

JavaFX 控件

JavaFX 控件是 UI 布局的构建块,它们位于javafx.scene.control包中作为一组 JavaFX API。它们是通过场景图中的节点构建的。它们可以通过 JavaFX CSS 进行主题和皮肤设置。它们可以在不同平台上进行移植。它们充分利用了 JavaFX 平台丰富的视觉特性。

这个图显示了目前支持的一些 UI 控件,还有更多未显示的:

JavaFX 控件

JavaFX UI 控件示例

注意

有关所有可用的 JavaFX UI 控件的更详细信息,请参阅docs.oracle.com/javase/8/javafx/user-interface-tutorial/ui_controls.htm#JFXUI336的官方教程和javafx.scene.control包的 API 文档。

Java SE 8 特性

我们将深入了解 Java SE 8 的两个最重要的特性 - lambda 或 lambda 表达式和功能接口,这使得 lambda 可用于我们,以帮助编写更好、更简洁、更低样板的 JavaFX 8 代码。但是,请记住,本书不会涉及每一个 lambda 细节,因为这不是一本 Java SE 8 的书。

注意

要更好地了解 Java 的 lambda 路线图,请访问以下官方教程:docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

Lambda 表达式

Java 语言项目lambda的主要目标是解决函数式编程的缺乏,并通过以类似于在 Java 中创建匿名对象而不是方法的方式轻松创建匿名(无名称)函数来提供一种轻松进行函数式编程的方法。

正如您在第一章的示例中所看到的,开始使用 JavaFX 8,我们讨论了在 JavaFX 按钮的按下事件上定义处理程序的常规方法,使用匿名内部类:

btn.setOnAction(new EventHandler<ActionEvent>() {
   @Override
   public void handle(ActionEvent event) {
     message.setText("Hello World! JavaFX style :)");
   }
});

与在按钮动作中设置消息文本字段的text属性的单行代码相比,这段代码非常冗长。能否重写这个包含逻辑的代码块而不需要那么多样板代码?

Java SE 8 通过 Lambda 表达式解决了这个问题:

btn.setOnAction(event -> {
    message.setText("Hello World! JavaFX style :)");
});

除了使您的代码更简洁和易于阅读外,Lambda 表达式还使您的代码执行性能更好。

语法

有两种编写 Lambda 表达式的方式,一般形式如下图所示:

语法

Lambda 表达式的一般形式 - 以创建新线程为例

这两种方式如下:

  • (param1, param2, ...) -> expression;

  • (param1, param2, ...) -> { /* code statements */ };

第一种形式,表达式形式,用于当我们只分配一行代码或只是一个简单表达式时。而第二种形式,块形式,是单行或多行代码的主体,带有返回语句,因此我们需要用大括号包裹它们。

以下三个语句是等价的:

  • btn.setOnAction((ActionEvent event) -> {message.setText("Hello World!");});

  • btn.setOnAction( (event) -> message.setText("Hello World!"));

  • btn.setOnAction(event -> message.setText("Hello World!"));

提示

要深入了解新的 lambda 表达式及其相关特性以及 Java SE 8 特性,我鼓励您尝试这个系列文章 - Java SE 8 新特性之旅:tamanmohamed.blogspot.com/2014/06/java-se-8-new-features-tour-big-change.html

函数接口

lambda 表达式很棒,不是吗?但是您可能想知道它的确切类型,以便将其分配给变量并传递给方法。

答案在于函数接口的强大。如何?函数接口是由 Java 语言设计师/架构师巧妙地创建为闭包,使用单一抽象方法SAM)的概念,提供了一个只有一个抽象方法的接口,并使用@FunctionalInterface注解。单一抽象方法模式是 Java SE 8 的 lambda 表达式的一个重要部分。

让我们通过一个示例来澄清函数接口和 lambda 表达式的概念。我创建了一个名为Calculator.java的函数接口,其中包含一个抽象方法calculate()。创建后,您可以声明并为 lambda 表达式分配变量。以下是函数接口:

@FunctionalInterface
public interface Calculator {
    double calculate(double width, double height);
}

现在我们准备创建变量并为它们分配 lambda 表达式。以下代码创建并为我们的函数接口变量分配 lambda 表达式:

Calculator area = (width, height) -> width * height; //Area = w × h
//Perimeter = 2(w+h)
Calculator perimeter = (width, height) -> 2 * (height + width);
out.println("Rectangle area: "+ area.calculate(4, 5)+" cm.");
out.println("Rectangle perimeter: "+ perimeter.calculate(4, 5)+" cm.");

代码的输出应该如下所示:

Rectangle area: 20.0 cm.
Rectangle perimeter: 18.0 cm.

主题

与设计师和 UX/UI 专家合作时,您会听到关于为应用程序设置皮肤或更改其外观的说法。这两个术语通常可以互换使用,它们都反映了主题的基本概念。

主题的理念是通过改变控件的外观而不改变其基本功能来改变整个应用程序的样式。

在 JavaFX 中,您可以创建、修改或使用现有的主题来为应用程序、场景甚至只是 UI 控件设置皮肤。

CSS

JavaFX 级联样式表CSS)可以应用于 JavaFX 场景图中的任何节点;它们是异步应用于节点的。样式也可以在运行时轻松地分配给场景,从而允许应用程序的外观动态变化。

它基于 W3C CSS 版本 2.1 规范,并且目前与版本 3 的当前工作中的一些附加功能兼容。JavaFX CSS 支持和扩展已经被设计为允许任何兼容的 CSS 解析器干净地解析 JavaFX CSS 样式表。这使得可以将 JavaFX 和其他目的(如 HTML 页面)的 CSS 样式混合到单个样式表中。

所有 JavaFX 属性名称都以-fx-为前缀,包括那些可能看起来与标准 HTML CSS 兼容的属性,因为一些 JavaFX 值与标准值的语义略有不同。

注意

有关 JavaFX CSS 的更多信息,请参阅使用 CSS 文档对 JavaFX 应用程序进行皮肤设置和docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html的参考指南。

应用 CSS 主题

这是一个自定义的简单 JavaFX CSS 规则,ButtonStyle.css,它将用于我们的主题过程来为按钮设置主题:

/* ButtonStyle.css */
.button {
-fx-text-fill: SKYBLUE;
-fx-border-color: rgba(255, 255, 255, .80);
-fx-border-radius: 8;
-fx-padding: 6 6 6 6;
-fx-font: bold italic 20pt "Arial";
}

我们有两种方法可以应用 CSS 样式表来改变我们的 JavaFX 应用程序的外观和主题:

  1. 使用 JavaFX 应用程序(javafx.application.Application)类的静态方法setUserAgentStylesheet(String URL)方法,可以为 JavaFX 应用程序中的所有场景和所有子节点设置样式。使用方法如下:
Application.setUserAgentStylesheet(getClass().getResource("ButtonStyle.css").toExternalForm());

现在您可以使用 JavaFX 8 当前预装的两个样式表,Caspian 和 Modena,我们可以使用与此处相同的方法在它们之间切换:

// Switch to JavaFX 2.x's CASPIAN Look and Feel.
Application.setUserAgentStylesheet(STYLESHEET_CASPIAN);

// Switch to JavaFX 8's Modena Look and Feel.
Application.setUserAgentStylesheet(STYLESHEET_MODENA);

提示

如果您通过传递空值来调用setUserAgentStylesheet(null),则将加载默认的外观和感觉(在这种情况下为 Modena),而如果您使用 JavaFX 2.x Caspian,则将加载默认的外观和感觉。

  1. 使用场景的getStylesheets().add(String URL)方法将自动为个别场景及其子节点设置样式,如下所示:
Application.setUserAgentStylesheet(null); // defaults to Modena
// apply custom look and feel to the scene.
scene.getStylesheets()
.add(getClass().getResource("ButtonStyle.css")
.toExternalForm());

基本上,将加载默认主题(Modena),因为调用了Application.setUserAgentStylesheet(null)。然后通过调用getStylesheets().add()方法设置场景的额外样式。

首先应用样式到父级,然后应用到其子级。节点在添加到场景图后进行样式设置,无论它是否显示。

JavaFX CSS 实现应用以下优先顺序 - 用户代理样式表的样式优先级低于从代码设置的值,后者优先级低于场景或父级样式表。

内联样式具有最高优先级。来自父级实例的样式表被认为比场景样式表的样式更具体。

Scene Builder

对于大多数复杂和复杂的 UI 需求,设计师使用工具在 WYSIWYG 界面中设计他们的 UI,而无需编写任何代码,然后将结果(FXML文件)加载到他们的 JavaFX 应用程序逻辑中会更容易吗?

因此,您需要 JavaFX Scene Builder;它是一个可视化布局工具,可以让您轻松地布置 UI 控件,以便您可以快速地使用效果和动画原型化您的应用程序。Scene Builder(2.0 及以上版本)是 JavaFX 8 的兼容版本。

在项目创建过程中的任何时候,您都可以预览您的工作,以检查其真实外观,然后再部署它。

它是开源的,因此与大多数 IDE 集成,但与 NetBeans IDE 更紧密。它还是一个跨平台的、独立的应用程序,可以在大多数平台上运行。

除了支持 CSS,它还允许您轻松地将自定义主题应用于您的原型。

下载和启动

2015 年初,Oracle 发布了 JavaFX Scene Builder 工具 2.0 版本,并宣布将不再提供 JavaFX Scene Builder 工具的构建(已编译形式)。

一家名为Gluongluonhq.com)的公司意识到工具可以改善或破坏编码体验。因此,他们决定开始提供基于他们将在公开可访问的存储库中维护的分支的构建。

Gluon 提供 IDE 插件,以及基于 OpenJFX 最新源代码的 JavaFX Scene Builder 工具的改进版本,还有基于社区参与和更好地支持第三方项目(如ControlsFX (www.controlsfx.org/)、FXyz (github.com/FXyz/FXyz)和DataFX (www.datafx.io/))的额外改进。

让我们从以下 URL 下载工具开始gluonhq.com/products/downloads/

下载版本 8.0 并安装后,启动它,Scene Builder 工具应该如下截图所示打开:

下载和启动

JavaFX 8 Scene Builder 工具。

FXML

在添加组件和构建美丽的 UI 布局时,Scene Builder 在幕后自动生成一个 FXML - 基于 XML 的标记文件,以便稍后将其绑定到 Java 应用程序逻辑的 UI。

FXML 提供的主要优势之一是关注点的分离,因为它将 UI 层(视图)与逻辑(控制器)解耦;这意味着您可以随时更改 UI 而不更改底层逻辑。由于 FXML 文件未经编译,因此可以在运行时动态加载,无需任何编译。这意味着它可以帮助您进行快速原型设计。

将 FXML 加载到 JavaFX 应用程序中

从 Scene Builder 工具中导出结果后,很容易将 UI 设计添加到其中。这里展示了在start()方法中加载 FXML 文件的代码:

BorderPane root = new BorderPane();
Parent content = FXMLLoader.load(getClass().getResource("filename.fxml"));
root.setCenter(content);

如您所见,我在javafx.fxml.FXMLLoaderclass上使用了load()静态方法,load()方法将加载(反序列化)由 Scene Builder 工具创建的 FXML 文件。

开发自定义 UI

在本章的最后部分,我们将基于 JavaFX 8 内置控件开发自定义 UI 组件。

我们将使用之前讨论过的基于 FXML 的概念来开发这个自定义 UI;其主要优势是关注点的分离,以便稍后定制组件而不改变其功能和与其绑定的任何其他逻辑。

登录对话框自定义 UI

我们将使用大部分之前介绍的工具和技术来开发我们的自定义 UI:登录对话框,这是每个企业应用程序中必不可少的组件。我们的 UI 组件将如下图所示:

登录对话框自定义 UI

登录自定义 UI 组件

登录对话框自定义 UI 的结构

基于 FXML 标记的自定义组件开发中最常见的结构和阶段如下:

  • 在 Scene Builder 工具中开发 UI;然后将结果导出为基于 FXML 的文件

  • 从 Scene Builder 中提取控制器骨架

  • 创建一个将 UI(视图)绑定到其逻辑并扩展控件或布局的控制器

  • 在 Controller 构造函数中加载 FXML 文件

  • 创建一个初始化方法,确保所有 FXML 控件都被成功初始化和加载

  • 公开公共属性以获取和设置控件数据和需要我们实现逻辑的动作方法

  • 开发一个单独的 CSS 文件

  • 在您的应用程序中使用自定义组件

编写登录对话框自定义 UI

让我们编写和开发我们的自定义 UI,登录对话框:

  1. 打开 Scene Builder 工具并创建 UI。其属性如下图所示:编写登录对话框自定义 UI

  2. 登录对话框布局层次结构如下所示:编写登录对话框自定义 UI

它由一个 Pane 布局作为顶部和根布局节点组成。然后,使用GridPane(1,4)来以一列和四行的网格布局放置控件,包括:

  • 第一行包含HBox布局控件,位置为(0,0),用于水平放置控件。它包括用于显示标志的ImageView控件和用于标题的 Label。

  • 第二行放置了用于用户名属性的TextField,位置为(0,1)。

  • 第三行放置了用于密码属性的PasswordField,位置为(0,2)。

  • 最后一行,位置为(0,3),有一个根布局控件HBox,它放置了另一个HBox,其中包含居中左对齐的CheckBoxLabel(用于显示错误和其他消息)。然后有两个按钮控件,重置登录,它们位于中心右侧。

  • 在代码选项卡中,为对话框中的所有控件添加适当的fx:id名称,并为按钮和复选框事件添加onAction名称,如下图所示:

编写登录对话框自定义 UI

登录按钮属性

  1. 从 Scene Builder 的预览菜单中,选择在窗口中显示预览。您的布局将弹出。如果一切正常,并且您对结果设计满意,从菜单栏中单击文件,然后单击保存,并输入文件名为LoginUI.fxml。恭喜!您已经创建了您的第一个 JavaFX UI 布局。

  2. 现在我们将打开 NetBeans 来设置一个 JavaFX FXML 项目,因此启动 NetBeans,并从文件菜单中选择新建项目

  3. JavaFX类别中,选择JavaFX FXML 应用程序。单击下一步。然后将项目命名为LoginControl,将FXML 名称更改为LoginUI,然后单击完成

提示

确保 JavaFX 平台是 Java SE 8。

  1. NetBeans 将创建如下的项目结构:编码登录对话框自定义 UI

登录控制 NetBeans 项目结构。

注意

在运行项目之前,请确保清理和构建您的项目,以避免可能出现的任何问题,特别是在运行应用程序和可能在运行时加载*.fxml文件时可能返回null

  1. 转到 Scene Builder 工具,并从视图中选择显示示例控制器骨架。将打开如下截图所示的窗口,我们将复制以替换LoginUIController.java(这将扩展Pane类内容代码与 NetBeans 中复制的内容)然后修复缺少的导入。

  2. 用 NetBeans 已经创建的一个替换之前生成并保存的LoginUI.fxml文件。

  3. 右键单击LoginController.java文件,选择重构,然后选择重命名,将其重命名为Main.java

  4. 最后,在Main.java 类start(Stage stage)方法中添加以下代码,如下所示。我们正在创建登录组件的新实例作为我们场景的根节点,并将其添加到舞台上:

LoginUIController loginPane = new LoginUIController();

stage.setScene(new Scene(loginPane));
stage.setTitle("Login Dialog Control");
stage.setWidth(500);
stage.setHeight(220);
stage.show();

编码登录对话框自定义 UI

  1. LoginUIController.java类中,在类名下右键单击选择插入代码;然后选择构造函数,最后在构造函数中添加以下代码:
public LoginUIController() throws IOException {
  FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("LoginUI.fxml"));
  fxmlLoader.setRoot(this);
  fxmlLoader.setController(this);
  fxmlLoader.load();
}

此代码加载我们的LoginUI.fxml文档,并将其作为 Pane 布局与其层次结构返回。然后将其绑定到当前控制器实例作为控制器和根节点。请注意,控制器扩展了 Pane 作为LoginUI.fxml中根元素定义。

  1. 从 NetBeans 中选择清理和构建,然后右键单击项目选择运行。应该出现与之前看到的相同的屏幕。

  2. 在程序运行时,输入任何凭据并单击登录按钮;如下截图所示,将出现红色的错误消息:编码登录对话框自定义 UI

登录控制无效登录。

  1. 如果输入正确的凭据(用户:tamanm,密码:Tamanm),则将显示绿色消息“有效凭据”,如下图所示。

  2. 如果单击重置按钮,则所有控件都将返回默认值。

恭喜!您已成功创建并实现了一个 UI 自定义控件。

编码登录对话框自定义 UI

摘要

在本章中,我们涵盖了很多内容-简要介绍了 JavaFX 8 架构组件,这些组件作为基础层来顺利高效地运行 JavaFX 应用程序。然后我们探讨了如何在场景上呈现图形,并简要解释了最常见的布局和 UI 控件。

您已经了解了 Java SE 8 中的新功能,例如 lambda 表达式和函数接口,这些功能得到了示例的支持,展示了每个功能的强大之处。

您学会了如何使用setUserAgentStylesheet(String URL)getStylesheets().add(String URL)方法来使用自定义 CSS 文件样式化您的应用程序。接下来,您简要了解了 Scene Builder 以及如何将 FXML 加载到场景中。最后,您学习了 JavaFX 中的自定义 UI 组件以及如何创建它们。

在下一章中,您将学习如何创建由多个场景组成的桌面应用程序,然后如何打包它。此外,我们还将学习如何与 Web 进行交互,并使用 JavaFX 8 开发 Web 应用程序。

第三章:开发 JavaFX 桌面和 Web 应用程序

本章将介绍如何开发引人注目的桌面和 Web 应用程序,利用多核、硬件加速的 GPU 来提供高性能的基于 UI 的应用程序,具有惊人的外观和感觉。

由于 JavaFX 完全是用 Java 从头开始编写的,一些 Java SE 8 内置的核心库将被用于支持我们的应用程序。此外,我们将学习如何将我们的应用程序打包为一个独立的应用程序进行启动和分发。

此外,我们还将涵盖 JavaFX 8 中任何 Web 应用程序中的基本核心 Web API,如javafx.scene.web.WebEnginejava.net.HttpURLConnectionjavafx.scene.web.WebView

我们将讨论 JavaFX 和 HTML5 之间的关系,这很重要,因为 JavaFX 的 API 和 HTML5 的特性互补。HTML5 是一个用于创建类似于本机桌面软件特性的用户体验的丰富 Web 内容平台。

更重要的是,我们将通过开发笔记应用程序的桌面版本,然后在 Web 上运行。

此外,我们将涵盖部署笔记作为 Web 应用程序所需的所有知识和技能,包括桌面和 Web。

在本章中将学到以下技能:

  • 开发和运行桌面和 Web 应用程序

  • 控制应用程序 UI

  • 如何打包 JavaFX 8 桌面应用程序

  • 在 JavaFX 应用程序中加载 HTML5 内容

  • 从 JavaFX 发送数据到 JavaScript,反之亦然

  • 部署 JavaFX Web 应用程序

开发一个笔记应用程序

仅为一个平台构建应用程序已经不够了。桌面、Web、移动和嵌入式支持对于成功的产品都是必需的,但学习不同的环境是困难的。这就是 JavaFX 的力量发挥作用的地方,它可以编写一个可以在不同平台上运行的应用程序,只需简单的调整,我们将在本章中看到。

在这里,我们将为桌面和 Web 构建一个笔记应用程序。在这个项目中,我将向您展示如何使用先前安装的开发工具(参见第一章,开始使用 JavaFX 8)从头开始使用 JavaFX 8 SDK 和 Java 编程语言创建完整的 JavaFX 应用程序。

然后我将向您展示如何创建应用程序的两个屏幕布局并创建控制它们的 Java 类。我将创建控制不同场景之间导航、保存数据的按钮,并使用属性绑定的功能动态更新您的 UI 控件。

最终项目将看起来像以下屏幕截图:

开发一个笔记应用程序

P'Note-Taking 应用程序

这张图显示了从主屏幕新建笔记按钮打开的添加和编辑屏幕,以添加新笔记或编辑列表中的一个笔记如下:

开发一个笔记应用程序

那么,您还在等什么呢?让我们开始吧!

构建 UI 原型

构建任何成功的应用程序的第一步(甚至是简单的应用程序)是原型化您的布局、屏幕关系、它们的状态和导航。在一张纸上草绘,然后从您的团队和经理那里获得反馈。重新修改,一旦获得批准,开始为您的客户构建一个真正的交互式原型,以便获得他们的反馈,以进行最终生产。

这就是我们现在要做的事情,我们的应用程序已经在易于使用的 UI 草图工具上布置在一张纸上,如下图所示。然后,我们将使用 Scene Builder 工具开发它作为一个完整的原型。

此外,我们将看到 NetBeans 和 Scene Builder 工具之间的互操作性。

注意

请注意,最好先在纸上画出布局草图,这是在与工具交互之前编辑、增强和找出最终应用程序布局的一种非常快速的方法。

现在,我们已经绘制了我们的应用程序,准备构建应用程序的真正原型。

最大限度地利用工具的最佳方法是在 NetBeans IDE 中创建应用程序骨架(控制器类和 FXML 基本页面定义),然后在 Scene Builder 工具中创建和开发 FXML 页面。这就是两个工具之间强大的互操作性。

构建 UI 原型

以下是开始使用 JavaFX FXML 应用程序的步骤:

  1. 打开 NetBeans IDE,从主菜单中选择文件,然后选择新建项目,将打开一个新项目对话框。从类别中选择JavaFX,然后在项目下选择 JavaFX FXML 应用程序。然后,点击下一步按钮:构建 UI 原型

一个新的 JavaFX FXML 应用程序

  1. JavaFX FXML 应用程序对话框中,添加相关信息。从项目名称中,添加位置和FXML 名称(在我的案例中为ListNotesUI)。在创建应用程序类中,我已添加packt.taman.jfx8.ch3.NoteTakingApp,如下图所示。点击完成构建 UI 原型

  2. 现在我们有了一个带有第一个 FXML UI 文档(ListNotesUI.fxml)的项目,我们需要添加第二个 FXML UI 文档(AddEditUI.fxml)以及其控制器。

  3. 要做到这一点,从文件中选择新建文件;然后,在类别列表下,选择JavaFX,从文件类型列表中选择空的 FXML,最后,点击下一步,如下图所示。

  4. 新建空的 FXML 和位置对话框中,将FXML 名称字段编辑为AddEditUI,然后点击下一步构建 UI 原型

添加一个新的空的 FXML 文档

  1. 在控制器类对话框中,勾选使用 Java 控制器复选框。确保已选择创建新控制器,并将控制器名称设置为AddEditUIController。然后,点击下一步,跳过级联样式表对话框,最后,点击完成构建 UI 原型

向 FXML 文档添加新控制器

当我们构建了项目结构后,就可以使用 Scene Builder 将控件添加到页面 UI 中,就像我们在纸上画的那样。这样做很容易:

  1. 从 NetBeans 中,右键单击ListNotesUI.fxml并选择打开,或者直接双击它。Scene Builder将以设计模式打开您的 FXML 文档。

注意

注意:仅当 Scene Builder 安装在您的计算机上时才有效。

  1. 根据以下截图设计页面。最重要的是,在返回 NetBeans 或关闭Scene Builder进行逻辑实现之前,不要忘记保存您的更改。构建 UI 原型

完成 ListNotesUI.fxml 文档设计

  1. AddEditUI.fxml执行相同的步骤,您的设计应该最终如下所示:构建 UI 原型

完成 AddEditUI.fxml 文档设计

您需要检查 FXML 文档,看看我们如何嵌套许多容器和 UI 控件,以实现我们之前在纸上草图中所期望的 UI,另外还要使用它们的属性来控制间距、对齐、字体和颜色。

恭喜!您已经将草图布局转换为可以呈现给团队领导和经理的项目,以获得有关颜色、主题和最终布局的反馈,而不需要逻辑。此外,一旦获得批准,您可以继续进行最终客户反馈,然后再深入业务逻辑。

让您的应用程序生动起来-添加交互

设计应用程序后,您需要通过使其更具交互性和响应性来使其更具生命力,以执行和响应客户提出的功能要求。

我总是首先在每个 FXML 文档控制器中添加页面导航处理程序,我已经在每个 FXML 文档控制器类中完成了这一点。

为了消除冗余并实现模块化,我在BaseController.java类中创建了一个基本导航方法,该方法将被系统中的所有控制器扩展。这个类将用于添加任何常见功能和共享属性。

以下方法navigate(Event event, URL fxmlDocName)是我们系统中所有导航中最重要的代码之一(注释说明了工作机制):

protected void navigate(Event event, URL fxmlDocName) throws IOException {
  //Loading new fxml UI document
  Parent pageParent = FXMLLoader.load(fxmlDocName);
  //Creating new scene
  Scene scene = new Scene(pageParent);
  //get current stage
  Stage appStage = (Stage)((Node) event.getSource()).getScene().getWindow();
  //Hide old stage
  appStage.hide(); // Optional
  //Set stage with new Scene
  appStage.setScene(scene);
  //Show up the stage
  appStage.show();
}

该方法将分别从ListNotesUI.fxml页面的New Note和编辑按钮的操作处理程序中调用ListNotesUIController.java,以及从AddEditUI.fxml页面的List Notes、保存和Cancel按钮的操作处理程序中调用AddEditUIController.java

注意 FXML 文档中定义的按钮与控制器之间的关系。@FXML注解在这里起作用,将 FXML 属性(使用#)与控制器中定义的操作绑定起来:

ListNotesUI.fxml文件中的New Note按钮定义如下:

<Button alignment="TOP_CENTER"
        contentDisplay="TEXT_ONLY"
        mnemonicParsing="false"
        onAction="#newNote" 
        text="New Note" 
        textAlignment="CENTER" 
        wrapText="true" 
/>

New Note操作在ListNotesUIController.java中定义,使用onAction="#newNote"绑定到前面的按钮:

@FXML
 private void newNote(ActionEvent event) throws IOException {
        editNote = null;
        navigate(event, ADD.getPage());
 }

AddEditUI.fxml文件中的Back按钮定义如下:

<Button alignment="TOP_CENTER"    
        contentDisplay="TEXT_ONLY"
        mnemonicParsing="false"
        onAction="#back" 
        text="Notes List" 
        textAlignment="CENTER"
        wrapText="true"
/>

Back操作在AddEditUIController.java中定义,使用onAction="#back"绑定到前面的按钮:

@FXML
private void back(ActionEvent event) throws IOException {
        navigate(event, FXMLPage.LIST.getPage());
}

您可能想知道FXMLPage.java类做什么。它是一个枚举(有关枚举的更多信息,请访问docs.oracle.com/javase/tutorial/java/javaOO/enum.html)。我已经创建了枚举来定义所有我们的 FXML 文档名称及其位置,以及与这些 FXML 文档相关的任何实用方法,以帮助简化我们系统中的编码。

提示

这种可维护性的概念有助于在大型系统中保持常量属性和功能在一个地方进行未来的重构,使我们能够在一个地方更改名称,而不是在整个系统中漫游以更改一个名称。

如果您检查系统控制器,您会发现处理其他按钮操作的所有逻辑 - 删除、编辑、清除和保存笔记。

使用属性实现应用程序更改同步

属性是 JavaFX 基于对象属性的包装对象,例如 String 或 Integer。属性允许您添加监听器代码,以在对象的包装值发生更改或被标记为无效时做出响应。此外,属性对象可以相互绑定。

绑定行为允许属性根据另一个属性的更改值更新或同步它们的值。

属性是包装对象,具有使值可读/可写或只读的能力。

简而言之,JavaFX 的属性是包装对象,保存实际值的同时提供更改支持、无效支持和绑定功能。我将在以后讨论绑定,但现在让我们来看看常用的属性类。

所有包装属性类都位于javafx.beans.property.* package命名空间中。以下是常用的属性类。要查看所有属性类,请参考 Javadoc 中的文档(docs.oracle.com/javase/8/javafx/api/index.html?javafx/beans/property.html)。

  • javafx.beans.property.SimpleBooleanProperty

  • javafx.beans.property.ReadOnlyBooleanWrapper

  • javafx.beans.property.SimpleIntegerProperty

  • javafx.beans.property.ReadOnlyIntegerWrapper

  • javafx.beans.property.SimpleDoubleProperty

  • javafx.beans.property.ReadOnlyDoubleWrapper

  • javafx.beans.property.SimpleStringProperty

  • javafx.beans.property.ReadOnlyStringWrapper

具有Simple前缀和Property后缀的属性是可读/可写属性类,而具有ReadOnly前缀和Wrapper后缀的类是只读属性。稍后,您将看到如何使用这些常用属性创建 JavaFX bean。

让我们快进到 JavaFX 的 Properties API,看看它如何处理常见问题。您可能会注意到TableView控件已经添加到主页面,列出了当前加载的笔记和任何新添加的笔记。

为了正确地填充TableView的数据,我们应该有一个数据模型来表示笔记数据,这是我在 JavaFX 的 JavaBean 风格的 Note 类中首次使用 Properties API 的地方,它定义如下:

public class Note {
    private final SimpleStringProperty title;
    private final SimpleStringProperty description;
    public Note(String title, String description) {
        this.title = new SimpleStringProperty(title);
        this.description = new SimpleStringProperty(description);
    }
    public String getTitle() {
        return title.get();
    }
    public void setTitle(String title) {
        this.title.set(title);
    }
    public String getDescription() {
        return description.get();
    }
    public void setDescription(String description) {
        this.description.set(description);
    }
}

为了使用应用程序数据库中已存储的数据填充TableView类,例如(我们这里的数据库是使用ObservableList<Note>来存储笔记对象的临时数据库),我们必须传递这些数据的集合。

我们需要摆脱手动更新 UI 控件(在我们的情况下是TableView控件)的负担,每当笔记数据集合被更新时。因此,我们需要一个解决方案来自动同步表格视图和笔记数据集合模型之间的更改,例如添加、更新或删除数据,而不需要从代码中对 UI 控件进行任何进一步的修改。只有数据模型集合被更新 - UI 应该自动同步。

这个特性已经是 JavaFX 集合的一个组成部分。我们将使用 JavaFX 的ObservableList类。ObservableList类是一个集合,能够在对象被添加、更新或移除时通知 UI 控件。

JavaFX 的ObservableList类通常用于列表 UI 控件,比如ListViewTableView。让我们看看我们将如何使用ObservableList集合类。

BaseController中,我已经创建了静态数据作为ObservableList<Note>,以便在所有控制器之间共享,以便能够向其中添加、更新和删除笔记。同时,它初始化了一些数据,如下所示:

protected static ObservableList<Note> data = FXCollections.<Note>observableArrayList(
  new Note("Note 1", "Description of note 41"),
    new Note("Note 2", "Description of note 32"),
    new Note("Note 3", "Description of note 23"),
    new Note("Note 4", "Description of note 14"));

ListNotesUIController.java类的initialize()方法中,我创建了一个javafx.collections.transformation.FilteredList类的实例,当我们在表格内容中搜索时,它将被用作过滤类。它将ObservableList<Note>类型的data对象作为源数据传递:

FilteredList<Note> filteredData = new FilteredList<>(data, n -> true);

FilteredList的第二个参数是用于过滤数据的谓词;在这里,它返回true,表示没有过滤,我们将在以后添加过滤谓词。

创建的ObservableList<Note>类型的数据列表应该传递给我们的TableView数据,以便表格视图监视当前数据集合的操作,比如添加、删除、编辑和过滤,就像在ListNotesUIController.java类的initialize()方法中所示的那样,但是我们传递了filteredData包装实例:

notesListTable.setItems(filteredData);

最后一步是确认我们的notesListTable列,类型为TableColumn,以及要呈现和处理 Note 类的哪个属性。我们使用setCellValueFactory()方法来完成,如下所示:

titleTc.setCellValueFactory(new PropertyValueFactory<>("title"));
descriptionTc.setCellValueFactory(new PropertyValueFactory<>("description"));

请注意,titledescriptionNote类的实例变量名称。

检查最终项目代码以获取完整的实现。然后,从 NetBeans 主菜单中运行应用程序,选择运行,然后点击运行主项目

尝试添加一个新的笔记,观察表格视图中您新添加的笔记。尝试选择和删除笔记或更新现有的笔记。您会立即注意到变化。

通过检查应用程序代码,您会发现我们所做的一切都是操纵数据列表,所有其他的同步工作都是通过ObservableList类来完成的。

过滤 TableView 数据列表

我们将在这里接触到两个最强大的 Java SE 8 和 JavaFX 8 功能PredicateFilteredList。让我们阐明我们手头的问题以及我们将如何使用stream功能来解决它。

在我们的ListNotesUI.fxml页面中,您可能会注意到位于笔记表格上方的文本字段;它在这里的目的是过滤当前表格数据,以缩小结果以获取特定的笔记。此外,我们需要维护当前的列表,小心不要从中删除任何数据或为每个搜索命中查询数据库。

我们已经有了笔记数据列表,我们将使用文本字段来过滤此列表中包含此字符或字符组合的任何笔记标题或描述,如下所示:

过滤 TableView 数据列表

填充了数据的表格

现在,在输入ddedevdevelopingJavaFX之后,表格将被过滤,如下截图所示。另外,尝试删除所有文本;您会发现数据会再次出现。接下来,我们将发现我们是如何做到的。

过滤 TableView 数据列表

使用搜索字段中的文本过滤表格数据

以下是完成此操作的神奇代码:

searchNotes.setOnKeyReleased(e ->
{
  filteredData.setPredicate(n ->
  {              
if (searchNotes.getText() == null || searchNotes.getText().isEmpty())
return true;

return n.getTitle().contains(searchNotes.getText())
|| n.getDescription().contains(searchNotes.getText());
  });
});

searchNotes是我们用来过滤笔记数据的文本字段的引用。我们已经使用setOnKeyReleased(EventHandler<? super KeyEvent> value)方法注册了它,一旦输入任何字符,就会获取我们的文本进行过滤。另外,请注意我们在这里使用了 Lambda 表达式,使代码更加简洁和清晰。

在动作方法的定义内部,filteredData是一个FilteredList<Note>类,我们已经传递了一个test()方法实现给setPredicate(Predicate<? super E> predicate),只过滤与searchNotes文本输入匹配的笔记标题或描述。

过滤后的数据会自动更新到表格 UI 中。

有关 Predicate API 的更多信息,请访问docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html

作为桌面应用程序的笔记

一旦您完成了应用程序,最好不要分发最终的 jar 文件,而是要求用户安装 JRE 环境以便能够运行您的应用程序,特别是如果您的目标是大众。

准备您的本机安装程序包作为.exe.msi.dmg.img更加专业。

每个安装程序都会管理应用程序的要求,从所需的资产和运行时环境。这确保了您的应用程序也可以在多个平台上运行。

为桌面分发部署应用程序

NetBeans 的一个高级功能是通过其部署处理程序为不同的平台打包您的应用程序,它提供以下主要功能:

  • 通过本机安装程序部署您的应用程序

  • 管理应用程序资产,如应用程序图标、启动画面和本机安装程序图标

  • 在准备最终包时,接受应用程序的最终签名证书

  • 管理所需的 JavaFX 运行时版本

  • 在 Windows 上使用开始菜单添加桌面快捷方式

  • 处理 Java Web Start 技术的要求和自定义

让我们看看 NetBeans 部署的配置:

为桌面分发部署应用程序

NetBeans 部署配置

要了解如何将您的应用程序打包成针对每个目标平台的本机安装程序,请访问以下网址,该网址提供了完成任务所需的所有步骤和软件:

netbeans.org/kb/docs/java/native_pkg.html

JavaFX 在 Web 上

在本节中,我们将学习有关 JavaFX 在 Web 上的知识,以及如何在那里部署我们的笔记应用程序。

WebEngine

JavaFX 提供了一个能够加载 HTML5 内容的非 GUI 组件,称为WebEngine API (javafx.scene.web.WebEngine)。这个 API 基本上是WebEngine类的对象实例,用于加载包含 HTML5 内容的文件。HTML5 文件可以从本地文件系统、Web 服务器或 JAR 文件中加载。

使用 Web 引擎对象加载文件时,会使用后台线程加载文件内容,以免阻塞JavaFX 应用程序线程

以下是两个用于加载 HTML5 内容的WebEngine方法:

  • load(String URL)

  • loadContent(String HTML)

WebView

JavaFX 提供了一个 GUI WebView (javafx.scene.web.WebView)节点,可以将 HTML5 内容呈现到场景图上。WebView节点基本上是一个迷你浏览器,能够响应 Web 事件,并允许开发人员与 HTML5 内容进行交互。

由于加载 Web 内容和显示 Web 内容的能力之间的密切关系,WebView节点对象还包含一个WebEngine实例。

JavaFX 8 的WebView类实现支持以下 HTML5 功能:

  • Canvas 和 SVG

  • 媒体播放

  • 表单控件

  • 历史维护

  • 交互元素标签

  • DOM

  • Web workers

  • Web sockets

  • Web 字体

WebView 和引擎的操作

我们将演示一个简单的示例,演示如何使用WebView将包含 Google 地图的 HTML5 网页文档集成到 JavaFX 中作为场景控件。然后,我们使用WebEngine从 JavaFX TextField控件中获取经度和纬度,执行一个 JavaScript 方法,将这些参数传递给地图,使地图居中显示在新传递的位置,并显示标记,如下图所示:

WebView and engine in action

JavaFX 8 应用程序中的 Google 地图查看器

为了清晰起见,我将只展示和解释代码中的重要部分,这演示了前面段落中提到的概念。有关本章中的完整代码,请查看web包代码GoogleMapViewerFX.java类和map.html文件。

要在 JavaFX 应用程序中查看 Google 地图,我们需要首先创建一个 HTML 文件,加载并集成 Maps API,这在map.html文件中定义。如前面的图片所示,位置居中于埃及开罗,我的城市,这是在创建地图时传递给地图的经度和纬度值,如下面的代码片段所示:

var latlng = new google.maps.LatLng(30.0594885, 31.2584644);
var Options = {
    zoom: 13,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("canvas"), Options);

接下来,我们要注意 JavaScript goToLocation(lng, lat)方法;这将从 JavaFX 应用程序中使用webEngine实例调用,根据从 JavaFX 控件中传递的经度和纬度来定位地图。

GoogleMapViewerFX.java中,我们创建了四个控件来组成我们的 UI - 两个用于经度和纬度的TextField类,一个更新按钮,以及一个用于查看map.html文档的WebView对象:

WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
final TextField latitude = new TextField("" + 29.8770037);
final TextField longitude = new TextField("" + 31.3154412);
Button update = new Button("Update");

请注意,我已经创建了带有初始经度和纬度的文本控件,这与原始地图位置不同。这个位置是我的家庭位置,你可以将它改为你的位置,然后点击更新以查看新位置。

要加载map.html文件,我们必须将其传递给我们已经创建的WebView类中创建的WebEngine类,如前面的代码片段所示。

实现按钮的onAction()方法,允许 JavaFX 控件和 JavaScript 之间的集成,使用webEngineexecuteScript()方法,如下面的代码所示:

update.setOnAction(evt -> {
   double lat = Double.parseDouble(latitude.getText());
   double lon = Double.parseDouble(longitude.getText());

   webEngine.executeScript("" +
             "window.lat = " + lat + ";" +
             "window.lon = " + lon + ";" +
             "document.goToLocation(window.lat, window.lon);");
});

运行应用程序,你应该看到前面的图像定位在开罗城!点击更新,你应该到达我的家,如下图所示。

尝试获取您的位置经度和纬度;然后也回到您的家!

很强大,不是吗?很容易集成 HTML5 内容,并与已经开发的 Web 应用程序进行交互,以在现有的 JavaFX 应用程序中添加更丰富的 Web 内容。

WebView 和 engine 在操作中

在 JavaFX 8 应用程序中更改 Google 地图位置

作为 Web 应用程序的笔记

一旦您的应用程序经过测试,就像我们之前讨论的那样,您可以将您的应用程序分发到多个平台和环境。我们已经在本章中使用分发的方式为桌面应用程序做了这样的操作,使用项目的dist文件夹下的.jar文件。

相同的.jar文件将用于 Web 部署,并且应用程序可以以多种方式部署为 Web 应用程序,我们将在接下来看到。

为 Web 运行应用程序

有三种方式可以在 Web 上运行您的 JavaFX 应用程序:

  1. 使用Java Web Start下载和启动应用程序一次;然后,您可以在离线状态下从您的计算机上使用它

  2. 将您的 JAR 嵌入到 HTML 文件中,以便在企业环境中运行

  3. WebEngine类加载 HTML 内容,并从WebView类中查看它,如前所述

Java Web Start

Java Web Start 软件提供了通过单击启动功能齐全的应用程序的能力。用户可以下载和启动应用程序,例如完整的电子表格程序或互联网聊天客户端,而无需经过冗长的安装过程。

使用 Java Web Start,用户可以通过单击 Web 页面上的链接来启动 Java 应用程序。该链接指向一个JNLP(Java 网络启动协议)文件,该文件指示 Java Web Start 下载、缓存和运行应用程序。

Java Web Start 为 Java 开发人员和用户提供了许多部署优势:

  • 使用 Java Web Start,您可以将单个 Java 应用程序放在 Web 服务器上,以便部署到包括 Windows、Linux 和 Solaris 在内的各种平台上。

  • 它支持 Java 平台的多个同时版本。应用程序可以请求特定版本的 Java Runtime Environment(JRE)软件,而不会与其他应用程序的需求发生冲突。

  • 用户可以创建一个桌面快捷方式来启动 Java Web Start 应用程序,而不需要浏览器。

  • Java Web Start 利用了 Java 平台固有的安全性。默认情况下,应用程序对本地磁盘和网络资源的访问受到限制。

  • 使用 Java Web Start 启动的应用程序会在本地缓存,以提高性能。

  • 对 Java Web Start 应用程序的更新在应用程序从用户的桌面独立运行时会自动下载。

Java Web Start 作为 JRE 软件的一部分安装。用户不需要单独安装 Java Web Start 或执行其他任务来使用 Java Web Start 应用程序。

有关Java Web Start的更多信息,请参阅以下链接:

为 Web 分发应用程序

要将您的 JavaFX 应用程序部署到 Web 上,可以使用 NetBeans 的一种非常简单的方法。

NetBeans 已经为您的 JavaFX 应用程序提供了三种部署类型 - 桌面、Java Web Start 和 Web,如下图所示:

为 Web 分发应用程序

总结

到目前为止,我们一直在学习如何为桌面和 Web 开发 JavaFX 企业应用程序。

在本章中,我们掌握了开发任何应用程序的技能,从在纸上草绘布局开始;接下来,我们将其转化为实际的交互式、丰富多彩的 UI 原型。我们看到了如何嵌套容器和控件以实现所需的布局。一旦我们获得了最终开发的批准,我们通过使其响应客户操作并提供功能要求,使应用程序栩栩如生。

我们利用 Java SE 8 功能和 JavaFX 绑定使我们的代码更加强大、干净和简洁。最后,我们学会了如何将我们的应用程序部署到目标桌面客户端或 Web 用户,以适应不同的平台和环境。

在下一章中,我们将学习如何为基于 Android 的智能手机开发 JavaFX 应用程序。此外,我们还将学习下载和安装 Android SDK 工具以及与记录器、模拟器和其他工具进行交互的必要技能,这些工具将帮助您进行任何与 JavaFX 无关的未来移动开发。

第四章:为 Android 开发 JavaFX 应用程序

毫无疑问,我们每天都在看到非 PC 客户端的增长。几乎每个人都至少有一部手机或平板电脑,可能来自不同的供应商,但肯定是带有 Android 或 iOS 的,因为它们在 2014 年占据了 96%的智能手机操作系统销售份额。

智能手机和平板电脑现在非常流行,这些数字每年都在增加。这就是为什么开发人员应该考虑获得开发这样一个巨大市场的应用程序所需的技能。

JavaFX 8 已经为 Web 和桌面提供了丰富的客户端应用程序,正如我们在第三章中所看到的,开发 JavaFX 桌面和 Web 应用程序。但是,如果像我一样,您已经编写了一个 JavaFX 应用程序,您肯定希望它能在尽可能多的设备上运行,以遵循Write Once, Run Anywhere的真正精神。我想借此机会告诉您,是的,我们可以在移动设备上运行 JavaFX。

基于 Oracle Corporation 对 JavaFX 的官方支持,JavaFX 社区内的许多人正在努力将 JavaFX 移植到尽可能多的设备和平台(桌面、移动和嵌入式)以及不同的操作系统上,使用相同的代码库。

他们已经成功地创建了 SDK,使我们能够开发 JavaFX 应用程序作为本机应用程序在 Android 或基于 iOS 的设备上运行在一个捆绑包中(JVM 加应用程序),而无需像在桌面或 Web 上运行它们一样需要任何额外的软件。

本章将为您提供关于 SDK 的基本实践知识,这将使您能够为 Android 创建、打包和部署本机应用程序。

在本章中将获得以下一些技能:

  • 安装和配置 Android 环境工具和软件

  • 准备和创建 JavaFX 8 移动项目结构

  • 创建一个 Android JavaFX 8 应用程序

  • JavaFX 8 与 Android 低级 API 之间的互操作性

  • 在移动设备上打包和部署应用程序

  • 为最终的 Google Play 商店提交应用程序进行签名

为什么要将 JavaFX 移植到移动环境?

为什么要将 JavaFX 移植到移动环境?这不是Write Once Run AnywhereWORA)吗?这是一个非常好的问题。任何 Java 应用程序都遵循 WORA 范例,但是有一个非常关键的抽象软件,它依赖于运行,被称为Java Virtual MachineJVM)。

JVM 是负责将编译的字节码(.class 文件)翻译为特定机器并提供平台指令以便它能理解和运行的软件,因此您可以运行您的应用程序。因此,您会发现为每种硬件(Intel、AMD、SPARC 或 ARM)和平台(Windows、Mac、Linux 或 Solaris)都有不同版本的 JRE 或JDK

在桌面、Web 或嵌入式设备上,您必须首先安装Java Runtime EnvironmentJRE)才能运行您的 Java 应用程序。但是,对于移动设备,您会注意到您只需从商店下载您的应用程序,安装它,最后运行它,而无需任何额外的软件。此外,一些封闭平台不允许安装 JVM。

为了更好的最终用户体验,运行 JavaFX 应用程序和运行其他针对 Android 或 iOS 的应用程序之间不应该有任何区别。

因此,我们应该有一个自包含的(应用程序加上 JVM)JavaFX 应用程序,可以在移动设备上运行。除了能够与 Android 低级 API 交互以控制设备功能外,它将被视为 Google Play 商店中的其他应用程序。

我们应该感谢社区提出这样的移植 SDK,并填补这一空白,使我们能够使用 RoboVM(www.robovm.org/)上的移植和使用 JavaFXPorts(javafxports.org/)上的移植在 iOS 上创建和运行我们的 JavaFX 应用程序,并在 Android 上创建和运行我们的 JavaFX 应用程序。

自 2015 年 2 月以来,这些项目背后的公司之间达成了协议,现在一个名为jfxmobile-plugin的单一插件允许我们从相同的代码库构建三个平台的应用程序:桌面、Android 和 iOS。

此外,一家名为Gluon的新公司提供了一个免费插件(gluonhq.com/products/tools/ide-plugins/),用于NetBeans,它创建了一个项目,其中包含构建基于jfxmobile-plugin的应用程序所需的一切。

注意

但请记住,所有这些都在不断发展,事情可能会有所变化。

它是如何工作的

RoboVM 用于 iOS 移植和 JavaFXPorts 用于 Android 移植都包含了所有必需的库,以便轻松打包您的 JavaFX 8 应用程序和所需的运行时环境。

使用 RoboVM 将 JavaFX 应用程序打包到 iOS(到.ipa包文件)时,所有 JavaFX 应用程序都会转换为Objective-C(目前是Swift)应用程序。

当使用 JavaFXPorts 将 JavaFX 应用程序打包到 Android(到.apk包文件)时,这些应用程序将被转换为在Dalvik VM 上运行的 Android 包。

这些 SDK 包含了大量的本地代码,将在将它们注入到您的 JavaFX 应用程序中后,被移植到 iOS 和 Android,以提高应用程序的性能。

使用这些 SDK,我们可以将我们的应用程序打包成适合提交到商店的格式(.ipa用于 iOS 和.apk用于 Android)。

谁在维护它?

不用担心 - 有大规模的免费支持,用于将 JavaFX 移植到 Android 和 iOS,以及商业支持。

注意

对于免费和商业支持,RoboVM 和 JavaFXPorts 社区都使用这个 Google 小组:

groups.google.com/forum/#!forum/javafxports

免费和商业支持主要来自社区中积极参与这两个项目的人,他们鼓励更多的第三方参与。

对于 iOS,RoboVM 为开发者提供了不同的计划;您可以在robovm.com/pricing/上查看。

而对于 Android,公司LodgON提供对 JavaFX-Android 集成的支持,作为他们对 JavaFX 移植的支持的一部分(www.lodgon.com/dali/page/JavaFX_Consulting)。

入门

我们现在已经有足够的信息,了解之前讨论的工具和 SDK 将如何让我们开始开发 JavaFX 应用程序,并将它们移植到 Android 移动设备上。

但在进入开发阶段之前,我们应该正确安装和配置工具和软件,以便根据提供的 SDK 完成开发过程,以便拥有最终的.apk包。

我们将在真实设备上部署这个.apk包,并最终对其进行签名,以便提交到 Google Play 商店。

因此,让我们开始安装先决工具和软件,以便开始开发我们的应用程序。

准备和安装先决软件

我们需要安装以下工具和软件列表,以便在没有任何问题的情况下完成我们的构建过程。

Java SE 8 JDK8 u45

我们以前已经做过这个了;参考第一章中的安装 Java SE 8 JDK部分,开始使用 JavaFX 8

注意

Java SE 8 更新 40 是为了开发 Android 的 JavaFX 应用程序所需的最低版本。

Gradle

从他们的网站上,这是 Gradle 的定义:

Gradle 是一个开源的构建自动化系统。Gradle 可以自动化构建、测试、发布、部署等软件包或其他类型的项目,比如生成的静态网站、生成的文档,或者其他任何东西。

最近,Android 开发工具将他们的构建系统更改为 Gradle。RoboVM 和 JavaFXPorts 移植项目模仿了相同的工具。

安装 Gradle 是一个非常简单的任务:

  1. 转到gradle.org

  2. 从右侧,在GET GRADLE!部分,点击Downloads 2.4(截至目前为止),下载过程将开始下载gradle-2.4-all.zip文件。

  3. 将下载的.zip文件复制到您选择的方便位置并解压缩它。

  4. 最后一步是将环境变量设置到您的系统中,如下所示:

  • 在 Windows 上 - 假设 Gradle 安装在c:\tools\gradle_2.4
set GRADLE_HOME=c:\tools\gradle_2.4
set PATH=%PATH%;%GRADLE_HOME%\bin

  • 在 Mac 上 - 假设 Gradle 安装在/usr/local/tools/gradle_2.4
export GRADLE_HOME=/usr/local/tools/gradle_2.4
export PATH=${PATH}:${GRADLE_HOME}/bin

Android SDK

Android SDK 包括 Android 平台的完整开发和调试工具集。

安装 Android SDK 是一个非常简单的任务:

  1. 转到developer.android.com/sdk/index.html#Other

  2. 在 SDK Tools Only 下,点击android-sdk_r24.2-{platform}.{exe|zip|tgz}(截至目前为止),针对您喜欢的平台的名称:Android SDK

  3. 将打开一个Download页面;接受条款,点击Download android-sdk_r24.2-{platform}.{exe|zip|tgz}按钮,下载过程将开始。

  4. 将下载的.zip文件复制到一个方便的位置并解压缩它,或者在 Windows 上双击.exe来开始安装。

  5. 从命令行运行以下命令:

$ android

Android SDK Manager 将打开;点击Build-tools version 21.1.2或更高版本以及 API 21 或更高版本的 SDK 平台。

点击Install x packages,接受许可证,然后点击Install。完成。

Android SDK Manager 的一个很好的参考资料在developer.android.com/sdk/installing/adding-packages.html

  1. 最后一步是在您的系统中设置环境变量,如下所示:
  • 在 Windows 上 - 假设 Android SDK 安装在c:\tools\android_ADT
set ANDROID_HOME=c:\tools\android_ADT\sdk
set PATH=%PATH%;%ANDROID_HOME%\platform-tools;%ANDROID_HOME%\tools

  • 在 Mac 上 - 假设 Android SDK 安装在/usr/local/tools/android_ADT
export ANDROID_HOME=/usr/local/tools/android_adt/sdk
export PATH=${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools

  • 这样做的最佳方法是在C:\Users\<user>\.gradle\gradle.properties下创建一个名为 ANDROID_HOME 的 Gradle 属性。

为 Android 准备项目

我们已经成功安装了先决条件软件和工具,并配置了环境变量,所以我们准备开始开发将被移植到 Android 设备的应用程序。

但在这之前,我们需要准备好我们的项目结构和构建文件,以便准备好使用 JavaFXPorts 库构建和打包我们的应用程序。

使用三种不同平台设置一个复杂的项目曾经是一项艰巨的任务,但最近 Gluon(http://gluonhq.com/)发布了一个 NetBeans 插件(gluonhq.com/gluon-plugin-for-netbeans/),大大简化了这项任务。

项目结构

最简单的方法是使用 NetBeans 的 Gluon 插件。这将为您创建一个 Java 项目,您只需要添加 JavaFX 源代码和一个带有所有任务准备的build.gradle文件。

安装了插件后,执行以下任务:

  1. 只需创建一个新的 JavaFX 项目,并选择Basic Gluon Application,如下所示:Project structure

  2. 为项目(DialPad2)、包(packt.taman.jfx8.ch4)和主类(DialPad2)选择有效的名称,您将在新项目中找到一堆文件夹。

  3. 遵循 Gluon 插件的顶部项目结构将带来更复杂的结构,并且应该如下截图所示:项目结构

Gluon 插件项目结构

接下来,我们将添加我们的构建脚本文件以完成我们的任务。

使用 Gradle

要构建一个 Gradle 项目,我们需要build.gradle脚本文件。Gluon 插件已经默认为您添加了此文件,包括所有属性,以允许我们的应用程序成功运行和编译。

默认的 Gradle 构建文件创建build.gradle文件应该如下所示:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.0.0-b8'
    }
}

apply plugin: 'org.javafxports.jfxmobile'

repositories {
    jcenter()
}

mainClassName = 'packt.taman.jfx8.ch4.DialPad2'

jfxmobile {

    android {
        manifest = 'lib/android/AndroidManifest.xml'
    }
}

唯一需要更改的重要事项是将jfxmobile-plugin版本更改为 1.0.0-b8(或最新版本;请经常查看bitbucket.org/javafxports/javafxmobile-plugin/overview以保持更新)。

该应用程序

您已经到达这一部分意味着我们已经正确设置了应用程序项目结构,并且现在已经准备好进行移动设备开发。

我们的应用程序将是一个新的智能手机拨号界面,用于在我们的设备上执行呼叫操作。它将使用 CSS 进行自定义以控制其外观样式,可以根据需要修改以获得不同平台的本机外观和感觉。

该应用程序的主要目的是提供一个新的 UI 概念(使用 CSS 自定义应用程序),您将学习如何使用 CSS id 和 class 选择器以及从代码内部设置它们以应用于不同的控件。

以下截图显示了应用程序在应用 CSS 文件之前和之后的样子:

应用程序

使用 CSS 开发和样式化应用程序 UI

正如我们之前学到的,我将开始原型设计我们的应用程序;在原型设计之后,我们应该有之前看到的应用程序 UI。

该应用程序 UI 直接写在DialPad2.java类的start(Stage)函数内,作为一种开发 UI 的替代方式,而不是使用静态的 FXML 设计。

在这里,我们从代码内部嵌套控件,以防需要动态生成 UI 控件并为它们分配不同的设置、CSS 类、id 选择器和监听器。

以下代码片段显示了我们如何生成前面的应用程序 UI:

BorderPane root = new BorderPane();
Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
Scene scene = new Scene(root, bounds.getWidth(), bounds.getHeight());
scene.getStylesheets().add(getClass().getResource("ui/Mobile_UI."+PlatformFactory.getName()+".css").toExternalForm());
TextField output = new TextField("");
output.setDisable(true);

root.setTop(output);
String[] keys = {"1", "2", "3",
                 "4", "5", "6",
                 "7", "8", "9",
                 "*", "0", "#"};

GridPane numPad = new GridPane();
numPad.setAlignment(Pos.CENTER);
numPad.getStyleClass().add("num-pad");
for (int i = 0; i < keys.length; i++) {
       Button button = new Button(keys[i]);
       button.getStyleClass().add("dial-num-btn");
       button.setOnAction(e -> output.setText(output.getText().concat(Button.class.
      cast(e.getSource()).getText())));
      numPad.add(button, i % 3, (int) Math.ceil(i / 3));
}
// Call button
Button call = new Button("Call");
call.setOnAction(e->PlatformFactory.getPlatform().callNumber(output.getText()));
call.setId("call-btn");
call.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
numPad.add(call, 0, 4);
GridPane.setColumnSpan(call, 3);
GridPane.setHgrow(call, Priority.ALWAYS);
root.setCenter(numPad);

//Stage setup
stage.setScene(scene);
stage.setTitle("Phone Dial v2.0");
stage.show();

代码首先创建一个以BorderPane为根节点的场景。创建场景后,代码加载 CSS 样式表文件Mobile_UI.<platform>.css,通过getStylesheets().add()方法来为当前场景的节点设置样式,如下所示:

scene.getStylesheets().add(getClass().getResource("ui/Mobile_UI."+PlatformFactory.getName()+".css").toExternalForm());

在我们创建了一个TextField输出来显示拨号结果并将其设置为禁用,以便我们无法编辑它之后,只需点击按钮即可添加和显示数字。

接下来,代码简单地使用GridPane类创建一个网格,并生成 12 个按钮放置在每个单元格中。请注意,在 for 循环中,每个按钮都通过getStyleClass().add()方法设置为名为dial-num-btn的样式类。

注意

在这里,我们使用了一个传统的for循环来添加按钮,而不是一个花哨的 Java 8 流。请注意,Dalvik VM仅在 Java 7 上运行,并且只能使用 lambda(因为在内部,JavaFXPorts 使用 Retrolambda 项目)。

最后,深蓝色的Call按钮将添加到网格窗格的最后一行。因为Call按钮是唯一的,它的 id 选择器设置为#call-btn,并且将使用 id 选择器进行样式设置,这意味着 CSS 文件中的选择器将以#符号为前缀。

以下是用于样式化应用程序的 CSS 文件:

.root {
    -fx-background-color: white;
    -fx-font-size: 20px;
    bright-green: rgb(59,223, 86);
    bluish-gray: rgb(189,218,230);
}
.num-pad {
    -fx-padding: 15px, 15px, 15px, 15px;
    -fx-hgap: 10px;
    -fx-vgap: 8px;
}

#call-btn {
    -fx-background-color: 
        #090a0c,
        linear-gradient(#38424b 0%, #1f2429 20%, #191d22 100%),
        linear-gradient(#20262b, #191d22),
        radial-gradient(center 50% 0%, radius 100%, rgba(114,131,148,0.9), rgba(255,255,255,0));
    -fx-background-radius: 5,4,3,5;
    -fx-background-insets: 0,1,2,0;
    -fx-text-fill: white;
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 );
    -fx-font-family: "Arial";
    -fx-text-fill: linear-gradient(white, #d0d0d0);
    -fx-font-size: 16px;
    -fx-padding: 10 20 10 20;
}
#call-btn .text {
    -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.9) , 1, 0.0 , 0 , 1 );
}

.dial-num-btn {
    -fx-background-color:
        linear-gradient(#f0ff35, #a9ff00),
        radial-gradient(center 50% -40%, radius 200%, #b8ee36 45%, #80c800 50%);
    -fx-background-radius: 30;
    -fx-background-insets: 0,1,1;
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.4) , 5, 0.0 , 0 , 1 );
    -fx-text-fill: #395306;
}

.dial-num-btn:hover {
    -fx-background-color: 
        #c3c4c4,
        linear-gradient(#d6d6d6 50%, white 100%),
        radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%, rgba(230,230,230,0) 50%);
    -fx-background-radius: 30;
    -fx-background-insets: 0,1,1;
    -fx-text-fill: black;
    -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 3, 0.0 , 0 , 1 );
}

.dial-num-btn:pressed {
    -fx-background-color: linear-gradient(#ff5400, #be1d00);
    -fx-background-radius: 30;
    -fx-background-insets: 0,1,1;
    -fx-text-fill: white;
}

有关 JavaFX 8 CSS 属性的更多信息,请访问以下 JavaFX 8 CSS 参考:

docs.oracle.com/javase/8/javafx/api/javafx/scene/doc-files/cssref.html

添加一些逻辑

正如您在代码片段中所看到的,每个 12 个按钮都有一个分配的操作,使用 lambda 表达式动态创建如下:

button.setOnAction(e -> output.setText(output.getText().concat(Button.class.cast(e.getSource()).getText())));

我们得到输出TextField并通过获取事件e的源来连接下一个数字、星号或井号,而在我们的情况下,这是点击的按钮,然后它的文本值,包含要拨打的号码。

使您的项目适用于移动设备

基本上,这个新项目是使用 Gluon 插件生成的(build.gradle更新到b8)。

为了使应用程序适用于移动设备,我们需要调整其高度和宽度以适应目标设备屏幕,并使 UI 树相应地做出响应。

这是一个非常简单但非常重要的步骤,我们可以通过将场景高度和宽度设置为目标设备屏幕尺寸来调整以下代码行。看一下以下行:

Scene scene = new Scene(root, 175, 300);

将此更改为以下代码行:

Rectangle2D bounds = Screen.getPrimary().getVisualBounds();
Scene scene = new Scene(root, bounds.getWidth(), bounds.getHeight());

第一行获取设备屏幕bounds。然后我们从这个边界变量设置场景高度和宽度。

第二行将您的源添加到源包[Java]和资源[Main]。然后添加一个PlatformFactory类,负责查找项目正在运行的平台。看一下具有方法签名的Platform接口:

public interface Platform {   
    void callNumber(String number);
}

这允许您在源上调用以下方法:

Button call = new Button("Call");
call.setOnAction(e-> PlatformFactory.getPlatform().callNumber(output.getText()));

最后,您为每个平台提供本机解决方案。例如,对于 Android:

public class AndroidPlatform implements Platform {

    @Override
    public void callNumber(String number) {
        if (!number.equals("")) {
            Uri uriNumber = Uri.parse("tel:" + number);
            Intent dial = new Intent(Intent.ACTION_CALL, uriNumber);
            FXActivity.getInstance().startActivity(dial);
         }
    }
}

为了使其在 Android 上工作,我们只需要修改AndroidManifest.xml,添加所需的权限和活动意图。这个自定义清单必须在build.gradle文件中引用,如下所示:

android {
    manifest = 'lib/android/AndroidManifest.xml'
  }

与低级 Android API 的互操作性

您需要android.jar来使用 Android API,并且您需要jfxdvk.jar来访问FXActivity类,它是JavaFXDalvik运行时之间的桥梁。我们在FXActivity上使用一个静态方法来检索FXActivity,它扩展了 AndroidContext。这个Context可以用来查找 Android 服务。

构建应用程序

为了为我们的应用程序创建 Android.apk 包文件,我们首先需要构建我们的应用程序;这是一个非常简单的任务。使用命令行(或从 NetBeans,右键单击项目选项卡,然后选择Tasks/task)指向当前项目文件夹,运行以下命令:

$ gradle build

Gradle 将下载所有所需的库并开始构建我们的应用程序。完成后,您应该看到成功的输出如下:

$ gradle build
Download https://jcenter.bintray.com/org/robovm/robovm-rt/1.0.0-beta-04/robovm-rt-1.0.0-beta-08.pom
:compileJava
:compileRetrolambdaMain
Download https://jcenter.bintray.com/net/orfjackal/retrolambda/retrolambda/1.8.0/retrolambda-1.8.0.pom
:processResources UP-TO-DATE
:classes
:compileDesktopJava UP-TO-DATE
:compileRetrolambdaDesktop SKIPPED
……...…
:check UP-TO-DATE
:build

BUILD SUCCESSFUL
Total time: 44.74 secs

到目前为止,我们已经成功构建了我们的应用程序。接下来,我们需要生成.apk 并将其部署到许多来源。

构建最终的.apk Android 包

在构建我们的.apk 文件时,有两个选项。第一个是通过运行以下命令:

gradle android 

这将在目录build/javafxports/android中生成.apk 文件。

第二个是通过运行此命令:

androidInstall 

这将在连接到您的台式机或笔记本设备的设备上部署生成的.apk 包。

我们将使用第一个选项(gradle android)来确保我们能够成功生成.apk 文件。成功完成后,您应该在先前提到的路径下有一个名为DialPad2.apk的文件。

部署应用程序

为了能够使用gradle androidInstall命令在连接的移动设备上部署我们的应用程序,您必须在设备上启用开发者选项并在其中启用一些其他设置,如下所示:

  1. 从您的设备上,点击设置打开设置菜单。

  2. 从顶部菜单中选择更多。选项取决于您的设备。

  3. 更多选项菜单列表的末尾,您应该看到开发者选项

  4. 点击开发者选项菜单。

  5. 通过在右上角打开滑块来启用开发者选项

  6. 测试提示

  7. 您必须在 Google Play 开发者中注册自己(play.google.com/apps/publish/),填写一个带有描述和几张截图的表格,最后提交 DialPad2 apk。

如果某些功能不如预期那样工作,请转到命令行并输入:

在交付应用程序之前最重要的一点是对其进行测试,特别是在不同的基于 Android 的移动设备上。

恭喜!您已完成——让我们去安装我们的应用程序。

发出此命令后,它将开始构建和打包 JavaFX 8 应用程序。插件将连接到您连接的设备并将应用程序安装到其中。这是您应该得到的结果:

$ gradle androidinstall

apk必须签名才能发布。签名意味着您需要一个私钥;为此,我们可以使用 keytool (developer.android.com/tools/publishing/app-signing.html#signing-manually)。

:compileJava
:compileRetrolambdaMain
………...…
:processAndroidResources UP-TO-DATE
:apk
:zipalign
:androidInstall
Installed on device.

BUILD SUCCESSFUL
Total time: 47.537 secs

在基于 Android 的设备上部署

在基于 Android 的设备上部署

为了在 Google Play 商店上部署您的应用程序,您必须执行以下操作:

点击DialPad2应用程序,您应该看到您的应用程序在设备上运行并完全按预期功能正常运行:

在 Google Play 商店上部署

根据我的经验,在移动测试领域中的四个黄金规则是:

JavaFX 8 应用程序拨打号码

发布意味着我们需要将签名配置添加到build.gradle中,如下所示:

可选:如果您看不到开发者选项,不要担心。它是存在的,但是隐藏的。这里是魔法——点击关于设备,找到构建号,然后点击 5 次(Lollipop 上点击 7 次)。您将看到一个数字倒计时,最后开发者选项将被启用。

尽可能在尽可能多的真实设备和 Android 平台上进行测试,以覆盖应用程序将在其中运行的所有情况,并了解其在生产中的行为。

$ adb logcat 

在基于 Android 的设备上部署

在基于 Android 的设备上部署

现在,我们准备运行以下命令:

  1. 在基于 Android 的设备上部署

  2. 右键单击DialPad2项目,从任务中选择apk,然后选择apkRelease

  3. 签署 APK

仅使用模拟器进行GUI 测试和功能,而不是进行性能测试。所有模拟器都依赖于您的底层 PC/笔记本硬件和内存,而在移动硬件上将会有很大的不同,要达到相同的性能将是非常具有挑战性的。

然后,您将获得设备上所有应用程序的输出。

调试下,启用USB 调试,点击允许 USB 调试警报窗口中的确定按钮,并启用未知来源

jfxmobile {
    android {
        signingConfig {
            storeFile file("path/to/my-release-key.keystore")
            storePassword 'STORE_PASSWORD'
            keyAlias 'KEY_ALIAS'
            keyPassword 'KEY_PASSWORD'
        }
        manifest = 'lib/android/AndroidManifest.xml'
        resDirectory = 'src/android/resources'
    }
}

现在打开您的设备,并从主屏幕上找到您的应用程序图标;在右下角,您应该看到您的DialPad2 JavaFX 应用程序已安装,如下截图所示,带有默认的 Android 图标:

点击呼叫按钮,将启动 Android 默认的拨号器,拨打您输入的号码,如下所示:

恭喜!生成的DialPad2.apk 已准备好提交到 Google Play 商店。

注意

在我与移动行业的经验中,我发现有数十家供应商的测试手机和平板电脑运行 Android 平台,它们每个都定制了每个设备的 UI 层,具有不同的功能和性能。

您还可以在application标签下添加您应用程序的图标(android:icon="@icons/ic_launcher)。这里,icons-*是带有几种分辨率的图像文件夹。

  1. 在 Android 设备上安装的 JavaFX 8 应用程序

  2. AndroidManifest.xml中,您必须通过在application标签上添加android:debuggable="false"来禁用调试选项。

  3. Chrome 有一个名为 ARC Welder 的新模拟器。请访问developer.chrome.com/apps/getstarted_arc查看。

  4. 最终生产和性能测试在真实设备上进行测试。这样您就可以确保您的应用程序在目标市场设备上能够正常运行。

总结

本章使您对移动行业有了很好的了解,以及如何使用不同的项目(如RoboVM用于iOSJavaFXPorts用于Android)开发和定制基于 JavaFX 的应用程序,从而使您的应用程序能够在两个平台上运行。

然后,我们学习了如何安装所需的软件和工具进行 Android 开发,并启用 Android SDK 以及JavaFXPorts库来打包和安装我们的拨号器基于 JavaFX 的应用程序到真实的 Android 设备,并将其提交到 Google Play 商店。

我们看到如何使用 CSS 自定义我们的应用程序,使同一应用程序具有不同的外观和感觉,以确保您为 Android 版本提供了不同的 CSS。

接下来,我们学习了如何将我们的设备调试模式启用,以便通过命令行成功安装来自jfxmobile-plugin的应用程序。最后,我们介绍了测试的四个黄金规则。

下一章与本章不会有太大的不同,但会很好地介绍和了解如何将您的 JavaFX 8 应用程序定位到运行在基于 iOS 的设备上。您还将学习如何使用其开发工具。

第五章:为 iOS 开发 JavaFX 应用程序

苹果在移动和 PC /笔记本世界拥有很大的市场份额,拥有许多不同的设备,从 iPhone 等手机到 iPod 等音乐设备和 iPad 等平板电脑。

它拥有一个快速增长的应用市场,称为 Apple Store,为其社区提供服务,可用应用程序的数量每天都在增加。移动应用程序开发人员应该为这样的市场做好准备。

同时针对 iOS 和 Android 的移动应用程序开发人员面临许多挑战。仅仅比较这两个平台的本机开发环境,就会发现它们存在实质性的差异。

根据苹果的说法,iOS 开发基于 Xcode IDE(developer.apple.com/xcode/)及其编程语言。传统上是 Objetive-C,2014 年 6 月,苹果推出了 Swift(developer.apple.com/swift/);另一方面,根据谷歌的定义,Android 开发基于 Intellij IDEA IDE 和 Java 编程语言。

没有多少开发人员精通两种环境。此外,这些差异排除了平台之间的任何代码重用。

JavaFX 8 正在填补平台之间可重用代码的差距,正如我们将在本章中看到的,通过在两个平台上共享相同的应用程序。

通过本章结束时,您将掌握以下一些技能:

  • 安装和配置 iOS 环境工具和软件

  • 创建 iOS JavaFX 8 应用程序

  • 模拟和调试 JavaFX 移动应用程序

  • 在 iOS 移动设备上打包和部署应用程序

使用 RoboVM 在 iOS 上运行 JavaFX

RoboVM 是从 Java 到 Objetive-C 的桥梁。使用它,开发运行在基于 iOS 的设备上的 JavaFX 8 应用程序变得容易,因为 RoboVM 项目的最终目标是解决这个问题,而不会影响开发人员体验或应用程序用户体验。

正如我们在前一章关于 Android 中看到的,使用 JavaFXPorts 生成 APK 是一个相对容易的任务,因为 Android 是基于 Java 和 Dalvik VM 的。

相反,iOS 没有为 Java 提供虚拟机,并且不允许动态加载本机库。

需要另一种方法。RoboVM 开源项目试图通过创建一个桥梁来解决 Java 开发人员的差距,该桥梁使用一个“提前编译”编译器,将 Java 字节码转换为本机 ARM 或 x86 机器代码。

特点

让我们来看看 RoboVM 的特点:

  • 将 Java 和其他 JVM 语言(如 Scala、Clojure 和 Groovy)带到基于 iOS 的设备上

  • 提前将 Java 字节码转换为机器代码,以便在 CPU 上直接进行快速执行,而不会产生任何开销

  • 主要目标是 iOS 和 ARM 处理器(32 位和 64 位),但也支持在 x86 CPU 上运行的 Mac OS X 和 Linux(32 位和 64 位)

  • 不对开发人员可访问的 Java 平台功能施加任何限制,如反射或文件 I/O

  • 支持标准的 JAR 文件,让开发人员重用第三方 Java 库的庞大生态系统

  • 通过 Java 到 Objective-C 桥接提供对完整本机 iOS API 的访问,实现具有真正本机 UI 和完整硬件访问的应用程序开发

  • 与 NetBeans、Eclipse、Intellij IDEA、Maven 和 Gradle 等最流行的工具集成

  • App Store 准备就绪,已有数百款应用程序在商店中

限制

主要是由于 iOS 平台的限制,使用 RoboVM 时存在一些限制:

  • 不支持在运行时加载自定义字节码。应用程序的所有类文件都必须在开发者机器上的编译时可用。

  • Java 本机接口技术通常在桌面或服务器上使用,从动态库加载本机代码,但是苹果不允许将自定义动态库与 iOS 应用一起发布。RoboVM 支持基于静态库的 JNI 变体。

  • 另一个重要限制是,RoboVM 是一个处于开发中的 Alpha 状态项目,目前尚不建议用于生产。

注意

RoboVM 完全支持反射。

工作原理

如第四章中所述,自 2015 年 2 月以来,RoboVM 和 JavaFXPorts 背后的公司之间已经达成协议,现在一个名为 jfxmobile-plugin 的单一插件允许我们从相同的代码库构建三个平台的应用程序-桌面、Android 和 iOS。

JavaFXMobile 插件为您的 Java 应用程序添加了许多任务,允许您创建可以提交到 Apple Store 的.ipa 包。

Android 主要使用 Java 作为主要开发语言,因此很容易将您的 JavaFX 8 代码与其合并。在 iOS 上,情况在内部完全不同,但使用类似的 Gradle 命令。

该插件将下载并安装 RoboVM 编译器,并使用 RoboVM 编译器命令在build/javafxports/ios中创建 iOS 应用程序。

入门

在本节中,您将学习如何使用JavaFXMobile插件安装 RoboVM 编译器,并通过重用我们之前在第四章中开发的相同应用程序 Phone Dial 版本 1.0,确保工具链正确工作,为 Android 开发 JavaFX 应用程序

先决条件

为了使用 RoboVM 编译器构建 iOS 应用程序,需要以下工具:

  • Oracle 的 Java SE JDK 8 更新 45。参考第一章,开始使用 JavaFX 8安装 Java SE 8 JDK部分。

  • 构建应用程序需要 Gradle 2.4 或更高版本的jfxmobile插件。参考第四章,为 Android 开发 JavaFX 应用程序安装 Gradle 2.4部分。

  • 运行Mac OS X 10.9 或更高版本的 Mac。

  • 来自 Mac App Store 的 Xcode 6.x(itunes.apple.com/us/app/xcode/id497799835?mt=12)。

提示

第一次安装Xcode,以及每次更新到新版本时,都必须打开它一次以同意 Xcode 条款。

为 iOS 准备项目

我们将重用我们之前在第四章中为 Android 平台开发的项目,因为在针对 iOS 时,代码、项目结构或 Gradle 构建脚本没有任何区别。

它们共享相同的属性和特性,但使用针对 iOS 开发的不同 Gradle 命令,并对 RoboVM 编译器的 Gradle 构建脚本进行了微小更改。

因此,我们将看到WORA 一次编写,到处运行的强大功能,使用相同的应用程序。

项目结构

基于第四章中 Android 示例的相同项目结构,为 Android 开发 JavaFX 应用程序,我们的 iOS 应用程序的项目结构应如下图所示:

项目结构

应用程序

我们将重用第四章中开发的相同应用程序,为 Android 开发 JavaFX 应用程序:Phone DialPad 版本 2.0 JavaFX 8 应用程序:

应用程序

正如您所看到的,重用相同的代码库是一个非常强大和有用的功能,特别是当您同时开发以针对许多移动平台,如 iOS 和 Android。

与低级 iOS API 的互操作性

为了具有与在 Android 中本机调用默认 iOS 电话拨号器相同的功能,我们必须提供 iOS 的本机解决方案,如以下IosPlatform实现:

import org.robovm.apple.foundation.NSURL;
import org.robovm.apple.uikit.UIApplication;
import packt.taman.jfx8.ch4.Platform;

public class IosPlatform implements Platform {

  @Override
  public void callNumber(String number) {
    if (!number.equals("")) {
      NSURL nsURL = new NSURL("telprompt://" + number);
      UIApplication.getSharedApplication().openURL(nsURL);
    }
  }
}

Gradle 构建文件

我们将使用与第四章中使用的相同的 Gradle 构建脚本文件,为 Android 开发 JavaFX 应用程序,但通过在脚本末尾添加以下行进行微小更改:

jfxmobile {
  ios {
    forceLinkClasses = [ 'packt.taman.jfx8.ch4.**.*' ]
  }
  android {
    manifest = 'lib/android/AndroidManifest.xml' 
  }
}

安装和使用robovm编译器的所有工作都由jfxmobile插件完成。

这些行的目的是为 RoboVM 编译器提供主应用程序类的位置,该类必须在运行时加载,因为默认情况下编译器看不到它。

forceLinkClasses属性确保在 RoboVM 编译期间链接这些类。

构建应用程序

在我们已经添加了必要的配置集以构建 iOS 脚本之后,现在是时候构建应用程序以将其部署到不同的 iOS 目标设备。为此,我们必须运行以下命令:

$ gradle build

我们应该有以下输出:

BUILD SUCCESSFUL

Total time: 44.74 secs

我们已经成功构建了我们的应用程序;接下来,我们需要生成.ipa文件,并且在生产环境中,您需要通过将其部署到尽可能多的 iOS 版本来测试它。

生成 iOS .ipa 软件包文件

为了为我们的 JavaFX 8 应用程序生成最终的.ipa iOS 软件包,这对于最终分发到任何设备或 AppStore 是必要的,您必须运行以下gradle命令:

gradle ios 

这将在目录build/javafxports/ios中生成.ipa文件。

部署应用程序

在开发过程中,我们需要在 iOS 模拟器上检查我们的应用程序 GUI 和最终应用程序原型,并在不同设备上测量应用程序的性能和功能。这些程序非常有用,特别是对于测试人员。

让我们看看在模拟器上运行我们的应用程序或在真实设备上运行是一个非常容易的任务。

部署到模拟器

在模拟器上,您可以简单地运行以下命令来检查您的应用程序是否正在运行:

$ gradle launchIPhoneSimulator 

此命令将打包并在iPhone 模拟器中启动应用程序,如下截图所示:

部署到模拟器

DialPad2 JavaFX 8 应用程序在 iOS 8.3/iPhone 4s 模拟器上运行

此命令将在 iPad 模拟器中启动应用程序:

$ gradle launchIPadSimulator 

部署到苹果设备

为了打包 JavaFX 8 应用程序并将其部署到苹果设备,只需运行以下命令:

$ gradle launchIOSDevice 

此命令将在连接到您的台式机/笔记本电脑的设备中启动 JavaFX 8 应用程序。

然后,一旦应用程序在您的设备上启动,输入任何号码,然后点击呼叫。

iPhone 将请求使用默认移动拨号器拨号;点击确定。默认移动拨号器将启动,并显示号码,如下图所示:

部署到苹果设备

默认移动拨号器

注意

要能够在您的设备上测试和部署您的应用程序,您需要与苹果开发者计划订阅。访问苹果开发者门户网站,developer.apple.com/register/index.action,进行注册。您还需要为开发配置您的设备。您可以在苹果开发者门户网站上找到有关设备配置的信息,或者按照此指南操作:www.bignerdranch.com/we-teach/how-to-prepare/ios-device-provisioning/

摘要

本章使我们对如何使用 RoboVM 开发和定制基于 JavaFX 的应用程序以在苹果平台上运行应用程序有了很好的理解。

您了解了 RoboVM 的特点和限制,以及它的工作原理;您还获得了用于开发的技能。

然后,您学会了如何安装 iOS 开发所需的软件和工具,以及如何启用 Xcode 以及 RoboVM 编译器,以在 OS 模拟器上打包和安装基于 JavaFX-8 的 Phone Dial 应用程序。

我们已经看到了如何重复使用我们在第四章中已经开发的相同应用程序,证明了 Java WORA 范式的有效性。

最后,我们提供了关于如何在真实设备上运行和部署应用程序的技巧。

下一章将为我们打开物联网开发世界的一扇窗户;我们将看到如何购买树莓派 2 型,安装和配置用于开发的 raspbian-wheezy 操作系统,以及如何安装用于嵌入式设备的 Java SE。然后,我们将开发一个 JavaFX 8 应用程序,该应用程序将在我们的信用卡大小的微型计算机上运行。

第六章:在树莓派上运行 JavaFX 应用程序

欢迎来到物联网Internet of Things)世界。毫无疑问,你总是听到这个术语。物联网最近成为一个热门话题,也是有充分理由的。一些估计将当前连接的小型设备数量约为 90 亿台;预计到 2020 年将跃升至 240 亿台。尽管预测各不相同,但评估确实如此:就数量而言,物联网将超越以往任何计算模型。

与物联网世界密切相关的是树莓派——由树莓派基金会设计的一款信用卡大小的微型计算机,用于实验和教育。

关于树莓派,你应该知道的是它只是一台小型计算机。小功率需求,小物理尺寸,小内存,最重要的是低成本设备。关于它的一切都很小,但它仍然只是一台计算机,它使用 Linux。

Java 从诞生的第一天起就是为物联网而生的。Java 的创造是有着明确的愿景:控制电视机顶盒等小型设备。随着物联网的爆发,Java 回归到了它的根基。

你可能会认为 JavaFX,一个用于丰富客户端开发的平台,会错过物联网的盛会——但事实并非如此!根据 Oracle 技术网络上的JavaFX 概述页面:

"它旨在提供一个轻量级、硬件加速的 Java UI 平台"

这个声明揭示了图形丰富和强大的 JavaFX 的关键:硬件加速;幸运的是,树莓派配备了强大的 GPU。

在这一章中,我们将学习关于:

  • 购买、准备和配置树莓派

  • 为 JavaFX 8 准备树莓派

  • 远程连接到树莓派

  • 在树莓派上安装和配置 Java SE 8

  • 在树莓派上开发和运行 JavaFX 8 应用程序

  • 使用 NetBeans 与树莓派

激动吗?需要玩得开心!好的,让我们直接开始玩我们的树莓派吧。

注意

自 2015 年 1 月发布 ARM 版本的 JDK 8u33 以来,Oracle 已经从 ARM 发行版中移除了 JavaFX 嵌入式。请参阅www.oracle.com/technetwork/java/javase/jdk-8u33-arm-relnotes-2406696.html#CACHGFJCjaxenter.com/jdk-arm-without-javafx-end-javafx-embedded-114212.html

JavaFX 嵌入式的代码已经提供给了开源项目 OpenJFX(wiki.openjdk.java.net/display/OpenJFX/Main)。建议寻找 JavaFX 嵌入式替代方案的开发人员加入并为该项目做出贡献。

在这一章中,我们将学习一些克服这个问题的方法。

什么是树莓派?

正如我们之前提到的,树莓派是一台非常小型和低成本的计算机。事实上,它大约是信用卡大小。不要被它的大小所欺骗;正如我们所知,好东西都是包装在小盒子里的。然而,树莓派根本没有包装。

它没有外壳,其电路板和芯片完全可见,如下图所示。你可以将树莓派插入数字电视或显示器,并使用 USB 键盘和鼠标,非常容易使用。由于其小巧的尺寸,你可以轻松地将它带到任何地方。

树莓派是一台功能强大的设备,可以让各个年龄段的人探索计算,并学习如何使用 Java、JavaFX、Python 和 Scratch 等语言进行编程。此外,它可以做任何台式电脑可以做的事情——从浏览互联网和播放高清视频或游戏到处理电子表格或文字处理软件。

什么是树莓派?

新的树莓派 2 型 B

你可以用它做什么?

树莓派为您提供了构建和控制设备的机会,使其按照您的意愿进行操作。例如,您可以部署自己的机器人手臂,由您编写的程序控制。您可以设计和创建自己的角色扮演游戏,或者通过编写代码制作美丽的计算机艺术或音乐。

此外,树莓派基金会的主要目标是让全世界的孩子们学会编程并了解计算机的工作原理,让学习变得有趣。

为什么树莓派是 JavaFX 的完美选择?

那么,树莓派为何如此适合 Java 和 JavaFX?答案可以从以下几点中找到:

  • 它比规格表所显示的要快。正如所述,树莓派的默认时钟速度为 900 MHz。但是,凭借其 900 MHz 的时钟速度,可以安全地超频到 1 GHz 以及 1GB 的 RAM,这台小型计算机可以运行更大更强大的应用程序。

  • 非常明智地,基金会选择了一个带有浮点支持的 CPU,通常被称为硬浮点,它比所谓的仅支持软浮点的芯片具有更高的性能。树莓派可用的操作系统利用了这种额外的功率和速度。

  • 最后,树莓派具有相当强大的图形处理单元(GPU),具有快速的 3D 核心,能够以 40MBits/s 的速度使用 H.264 进行蓝光质量的播放(www.raspberrypi.org/help/faqs/#generalSoCUsed)。

您应该购买哪个模块?

在撰写本文时,树莓派有五个型号:A,A+,B,B+,以及自 2015 年 2 月以来的新型号 Pi 2 B 型号。以下是 A+和 2B 型号之间的比较。

A+型号 2B 型号
成本约 25 美元 成本约 35 美元
一个 USB 端口 四个 USB 端口
没有以太网 标准以太网连接
256MB RAM 1GB RAM

A+型号更便宜,但只有一个 USB 端口和没有以太网连接。这可能不是问题。如果您将一个带电源的 USB 集线器连接到 A+型号,然后使用 USB 到 WiFi 适配器,您就拥有了 B+型号的所有网络功能。两个型号之间的一个主要区别是 RAM 的数量。A+型号有 256MB 的 RAM。B+型号有 512MB 的 RAM,2B 型号有 1GB 的 RAM。这两个型号都无法升级。

所有树莓派微型计算机都配备了一个 SD 存储卡插槽,音频输出插孔,RCA 和 HDMI 的视频端口,以及一排用于通用输入和输出的引脚。还有两个用于显示和摄像头的附加连接器,但两者都需要高度专门化的硬件。鉴于价格上的小差异,通常为 10 到 25 美元,我建议首先购买 2B 型号。如果您要购买多个,比如用于教室,A+型号可能就足够了。

您可以从任何在线商店购买一个包含所有所需物品的套件,价格不会超过 100 美元,其中包括:

  • 新的树莓派 2(RPi2)四核 900 MHz 1GB RAM 和 CanaKit WiFi 适配器

  • 高品质的 6 英尺 HDMI 电缆,GPIO 到面包板接口板,排线,面包板,跳线,GPIO 快速参考卡和电阻颜色快速参考卡

  • 8GB 三星 MicroSD 卡(树莓派基金会推荐的预装有 NOOBS 的 MicroSD 卡),高质量的树莓派 2 外壳和散热片

  • RGB LED,8 个 LED(蓝色/红色/黄色/绿色),15 个电阻,2 个按钮开关,以及初学者电子元件通用指南

  • 2.5A USB 电源适配器,带 5 英尺的 micro USB 电缆,专为树莓派 2 设计(UL 认证)

您应该购买哪个模块?

树莓派 B 型 2 的典型套件组件

购买树莓派

英国的树莓派基金会制造了树莓派。不幸的是,它有一段时间的缺货历史。幸运的是,您可以从几家供应商那里购买,其中一些列在www.raspberrypi.org/的主页上。您也可以从www.amazon.com购买,尽管价格会稍高一些。价格会有所不同。

最后,查看www.adafruit.com。它们价格合理,还提供一些您未来项目中需要的有用配件。在这些商店中,您还可以找到包括树莓派和启动所需组件的入门套件。

相关网站和文档

互联网上有大量关于树莓派的信息。当您研究更高级的主题时,知道在哪里找到答案将会很有帮助。

树莓派基金会的官方网站是www.rasberrypi.org。它列出了购买树莓派微型计算机的来源。它有各种教程和有用的论坛。

有关树莓派上运行的 Linux 版本的更多信息,请访问elinux.org/index.php?title=RPi_Hub&redirect=no。这里有关于通用和输入/输出引脚的信息;Raspbian Wheezy,专为树莓派设计的 Linux 版本;以及示例项目的信息。您还会找到有关其他嵌入式系统的信息,如Minnow boardBeagleBoard

Neil Black 创建了一份出色的树莓派初学者指南,值得一致好评。如果在设置过程中感到困惑,请访问neil-black.co.uk/the-updated-raspberry-pi-beginners-guide

最后,访问www.adafruit.com购买树莓派以及电源适配器、电机控制板和实验套件。如果您无法在当地购买零件,这个网站是购买配件和其他组件的绝佳地方。

为 JavaFX 8 准备树莓派

没有操作系统,您的树莓派将无法运行,操作系统是从 SD 卡加载的。我们需要一种方法来与之交互,首先安装支持的操作系统,我们的情况下是 Raspbian Wheezy;所有 Pi 的官方支持操作系统都在链接www.raspberrypi.org/downloads/上列出并可从中下载。

然后,我们将配置我们的 Pi 的网络设置,以便远程连接。最后,我们将检查默认安装的 Java SE 8 版本,并继续检查更新,如果操作系统没有预先打包。

如前所述,最新更新不包括 JavaFX,因此我们将找到一种方法来添加它。让我们开始准备我们的 SD 卡,安装 Raspbian Wheezy 操作系统,让树莓派运行起来。

创建可启动的 SD 卡

现在,我们将准备我们的 SD 卡,安装 Raspbian Wheezy 操作系统,这将允许我们与我们的树莓派进行交互。这是一个非常重要的步骤。有两种方法可以做到这一点:

使用 NOOBS

NOOBS 是一个简单的操作系统安装程序,其中包含 Raspbian。但是精简版不包含 Raspbian。它还提供了一系列备选操作系统,然后从互联网上下载并安装。

初学者应该从 NOOBS 方法开始,但它需要一个速度良好的互联网连接来下载首选操作系统。

如果你购买的套件配有预装的 NOOBS SD 卡,你可以跳到下一步。或者,如果你需要一个 SD 卡,你可以从 Swag 商店swag.raspberrypi.org/products/noobs-8gb-sd-card订购,甚至自己下载并设置到你的 SD 卡上。所有步骤都在链接www.raspberrypi.org/help/noobs-setup/中提供。

将 Raspbian Wheezy 操作系统烧录到你的 SD 卡:

这是我最喜欢的设置,因为我已经下载了操作系统,将直接将其烧录到我的 SD 卡上;以下是在 Mac OS X 上执行此操作的步骤(确保你有一个有效的 SD 卡,容量为 4/8/16GB,等级为 10):

我们需要将 SD 卡格式化为 FAT32。我们可以使用 SD Formatter 4.0 轻松实现这一点,它适用于 Windows 或 Mac,可以从 SD 协会的网站www.sdcard.org/downloads/formatter_4/eula_mac/index.html下载。

按照安装软件包的说明进行操作:

  1. 将你的 SD 卡插入计算机或笔记本电脑的 SD 卡读卡器,并记下分配给它的驱动器号—例如,在我的情况下是/disk2

  2. SDFormatter中,选择你的 SD 卡的驱动器号,转到格式选项并选择覆盖格式,命名为RaspWheezy(可选),然后点击格式化。根据卡的大小,格式化 SD 可能需要一些时间。使用 NOOBS

使用 SDFormatter 应用程序格式化 SD 卡

  1. 格式化完成后,关闭 SDFormatter。如果你在 Mac 或 Linux 上,从终端运行以下命令行来检查磁盘号和格式类型:
$ diskutil list

在这种情况下,SD 卡是/dev/disk2,格式类型为DOS_FAT_32,名称为RASPWHEEZY。在 Windows 上,打开 Windows 资源管理器并检查驱动器。

注意

不要搞错了,否则可能会破坏错误的磁盘/卡/驱动器上的所有数据。

  1. 从链接downloads.raspberrypi.org/raspbian_latest下载 Raspbian Wheezy 操作系统,解压缩,你应该会得到2015-02-16-raspbian-wheezy.img文件。

  2. 在 Mac 或 Linux 的命令行上,卸载磁盘但不要弹出:

$ diskutil unmountDisk /dev/disk2

  1. 然后使用dd命令将镜像写入 SD 卡:
$ sudo dd if=/path/to/2015-02-16-raspbian-wheezy.img of=/dev/rdisk2 bs=1m

输入密码后,写入过程开始,你需要等待直到再次获得提示。由于这将需要几分钟,在 Windows 上,你可以使用 Win32DiskImager(可以从www.raspberry-projects.com/pi/pi-operating-systems/win32diskimager下载)。

  1. dd命令完成后,弹出卡:
$ sudo diskutil eject /dev/rdisk2

注意

请注意,dd在没有错误或完成之前不会反馈任何信息;完成后将显示信息并重新挂载磁盘。但是,如果你希望查看进度,可以使用Ctrl + T快捷键。这会生成SIGINFO,你的tty的状态参数,并显示有关该进程的信息。

恭喜,现在将你的 SD 卡安装到树莓派上,并连接到合适的显示器上启动它。

配置树莓派

现在,我们需要为第一次启动设置 Pi,并配置一个静态 IP 以便从我们的笔记本电脑和远程连接到它:

  1. 挂载我们之前准备好的 SD 卡。

  2. 连接键盘、鼠标和显示器电缆。

  3. 将 WiFi 适配器插入其中一个 USB 端口。

  4. 现在,将电源线插入 Pi。

  5. 你应该在屏幕上看到一些详细的输出,启动 Raspbian 操作系统。大胆前行,毫无畏惧。

  6. 在第一次启动时,树莓派配置屏幕将显示,并为你提供一系列选项,你可以用它们来配置你的树莓派。基本上,你会想要设置你的时区和本地配置。查看在 CPU 和 GPU 之间的内存分配设置,或者启用 SSH。但在大部分情况下,你可以简单地忽略它们,用箭头键移动到最后一步,然后按回车键。

  7. 如果在配置过程中选择了你不喜欢的东西,你可以通过在控制台中输入sudo raspi-config来重新启动配置。

  8. 如果树莓派配置正确,你会看到一系列 Linux 启动消息滚动,然后会出现一个登录请求。默认用户登录是pi,密码是raspberry。现在,你将看到一个标准的 Linux 提示符。恭喜,你的树莓派已经启动运行。

  9. Wheezy 带有图形用户界面。只需输入sudo startx,你就会看到一个色彩丰富的用户界面,包括游戏、文字处理器和网页浏览器,如下面的截图所示:配置树莓派

树莓派桌面

树莓派桌面是轻量级 X11 桌面环境LXDE)。花一些时间来探索它。你会发现它非常熟悉,尽管比你的高性能台式电脑慢一些。

当你完成 LXDE 后,只需注销,你就会回到 Linux 提示符。为了保存在 SD 卡上的信息,优雅地关闭你的树莓派是很重要的。在你拔掉电源线之前,发出一个关闭命令:

$ Sudo shutdown -h now.

这将确保在关闭所有进程之前将所有内容写入 SD 卡。现在,你可以安全地拔掉电源线,这就是树莓派的开关的全部功能。

恭喜,你已经完成了你的第一个树莓派会话。

远程连接到树莓派

通常,你会使用外围设备和显示器连接到你的树莓派,但这并不总是情况,因为在开发阶段或树莓派本身被用作控制家用电器的酷炫服务器时,你需要从你的计算机、浏览器甚至移动设备上控制你的树莓派。

给树莓派分配固定的网络地址并不是必需的,但强烈建议这样做。这样做意味着你总是使用相同的地址(或名称,如果你在主机文件中创建了一个条目)连接到你的树莓派,因此它会从你的开发过程中删除一个潜在的变量。

更新网络 DHCP 设备/路由器与树莓派的 IP 地址也是一个好主意,这样它就不会尝试将其分配给网络上的另一个设备。执行此操作所需的步骤将因交换机/路由器制造商而异。

我们将在树莓派上安装 VNC 服务器。虚拟网络计算VNC)允许你通过网络控制一台计算机。它提供了一个图形用户界面,包括鼠标和键盘。在我们的情况下,它将允许我们看到和使用树莓派的 GUI,而无需连接到树莓派的物理键盘和鼠标。

目前,这是一个便利,如果你对当前的鼠标、键盘和显示器设置满意,你可以跳过这一部分。当你开始尝试需要一个或多个 USB 端口的设备时,VNC 将成为必需品。

设置 VNC 有五个步骤:

  1. 连接到家庭 WiFi 互联网。

  2. 在树莓派上安装 VNC。

  3. 设置开机启动。

  4. 设置静态 IP 地址。

  5. 使用客户端连接 VNC。

远程连接到 WiFi 互联网,Raspbian Wheezy 包括一个 WiFi 配置实用程序。此外,2012 年 10 月 28 日之后发布的所有 Raspbian 都预装了此实用程序。

注意

设置 WiFi 要求你的路由器正在广播 SSID。确保你的路由器上设置了广播 SSID!这不适用于私人 SSID 设置。

现在,让我们远程连接树莓派:

  1. 从 Raspbian 桌面,转到菜单 | 首选项 | WiFi 配置,如下屏幕截图所示:远程连接树莓派

选择 WiFi 配置实用程序

  1. 双击图标,您将看到以下窗口:远程连接树莓派

WiFi 配置实用程序 GUI

  1. 单击扫描按钮,将打开第二个窗口。在列表中找到您的无线接入点,并双击它。这将打开另一个窗口:远程连接树莓派

接入点列表

  1. PSK字段中输入密码,然后单击添加。当您查看第一个窗口时,您应该看到连接已经设置好可以使用。远程连接树莓派

添加接入点的最终状态

您可以使用按钮连接或断开连接。您可以在前面的屏幕截图中看到树莓派的 IP 地址显示在窗口底部。

请注意,有一个手动程序可以在终端上设置 WiFi 连接。这需要编辑config文件并手动添加网络的 SSID 和密码。有关更多信息,请访问www.raspberrypi.org/documentation/configuration/wireless/wireless-cli.md

恭喜,您的树莓派已连接到互联网。现在让我们安装 VNC 服务器。

在树莓派上安装 VNC

现在您已经连接到互联网,可以在树莓派上安装 VNC 服务器。如果您使用的是 Raspbian Wheezy,这很简单。在命令提示符下,输入以下命令:

$ sudo apt-get install tightvncserver

您将收到消息:您想继续吗?是或否?

让我们用大写Y回答并休息一下。安装完成后,输入以下命令:

$ vncserver

您将被要求创建一个密码,我使用raspberry。它指出密码长度超过八个字符;继续重新输入raspberry。接下来,您将被问到:您想输入只读密码吗?输入N表示否。

恭喜,您已在树莓派上运行 VNC。

设置 VNC 在启动时启动

随着您变得更加高级,您可能并不总是需要 VNC,但让我们假设您希望每次启动树莓派时都运行 VNC:

  1. 使用以下命令从 Pi LX 终端编辑rc.local文件:
$ sudo nano /etc/rc.local

  1. 滚动到底部,在exit 0上面添加以下行:
su -c "/usr/bin/tightvncserver -geometry 1280x1024" pi

  1. 保存文件并使用以下命令重新启动树莓派:
$ sudo shutdown -r now

  1. 现在,每次启动树莓派时,VNC 都将可用。

设置静态 IP 地址

通过 VNC 连接树莓派需要一个静态 IP 地址,即不会更改的 IP 地址。我将向您展示如何在接下来的几个步骤中为有线和无线网络获取静态 IP 地址:

  1. 如果您在家庭网络上,您需要发现一个可用的 IP 地址。为此,转到您的树莓派,打开 Pi LX 终端,然后输入:
mohamed_taman$ ifconfig –a

然后,输入以下命令:

mohamed_taman$ netstat -nr

  1. 收集以下信息:当前 IP(如果您想保留它),子网掩码网关目的地广播。记下这些,您很快会需要它们!

  2. 在树莓派上,通过运行以下命令备份/etc/network/interfaces

$ sudo cp /etc/network/interfaces /etc/network/interfaces.org

  1. 使用以下命令修改interfaces文件:
$ sudo nano /etc/network/interfaces

  1. 从以下更改interfaces文件:设置静态 IP 地址

编辑前的 interfaces 文件

  1. 选择适合您网络的 IP 号码;还要将wpa-ssid更改为您的无线网络名称,将wpa-psk更改为无线密码:设置静态 IP 地址

编辑后的 interfaces 文件

  1. 保存文件并重新启动树莓派。这些设置适用于有线和无线连接。恭喜,您现在可以使用 VNC 客户端连接到您的树莓派。

树莓派的自动登录

像大多数人一样,你可能买了树莓派来为家庭或办公室构建自己的设备。接下来你应该做的是设置树莓派,连接你的外围设备,并安装或开发必要的软件。

你在项目结束时想要的是打开设备并看到你期望的所有魔术。

当 Pi 引导到登录提示并等待你输入用户名和密码时,问题就来了。所以,让我们自动化树莓派登录:

  1. 从你的 Pi 上,打开一个终端并使用以下命令编辑inittab文件:
sudo nano /etc/inittab

  1. 通过导航到inittab中的以下行来禁用getty程序:
1:2345:respawn:/sbin/getty 115200 tty1

  1. 在该行的开头添加#来注释掉它,如下一行所示:
#1:2345:respawn:/sbin/getty 115200 tty1

  1. 在注释行下方添加一个登录程序到inittab
1:2345:respawn:/bin/login -f pi tty1 </dev/tty1 >/dev/tty1 2>&1

  1. 这将使用pi用户运行登录程序,而无需任何身份验证。

  2. Ctrl + X保存并退出,然后按Y保存文件,然后按Enter确认文件名。

重新启动 Pi,它将直接引导到 shell 提示符pi@raspberrypi,而不会提示你输入用户名或密码。

使用客户端连接 VNC

在继续之前,让我们确保一切都正常工作。为此,你需要一个 VNC 客户端。如果你使用的是带有最新版本 Mac OS X 的 Macintosh,这很简单。

转到Finder | 前往 | 连接到服务器。输入vnc://和你给树莓派分配的 IP 地址。在我的情况下,是 192.168.2.150 后跟一个冒号和数字 5901,如下截图所示。完整的 URL 应该是vnc://192.168.2.150:5901

使用客户端连接 VNC

连接到 Pi VNC 服务器。

如图所示,5901是树莓派 VNC 服务器正在监听的端口号。点击连接。不用担心屏幕共享加密,再次点击连接。现在输入之前创建的密码(raspberry)。如果一切正常,你会看到一个大的树莓。恭喜!

如果你不是在 Macintosh 电脑上,你需要下载一个 VNC 客户端。你可以从realvnc.com/获取免费的查看器。有 Windows、iOS、Android 和 Chrome 浏览器的客户端。是的,你可以用手机控制你的树莓派。

JavaFX 8 开发先决条件

现在,我们已经为开发设置和配置了我们的树莓派,我们需要在我们的开发机器和 Pi 上安装相同正确匹配的 JDK 8 构建版本。这对于在运行我们的 JavaFX 8 应用程序时避免库/版本问题非常重要,这就是我们接下来要做的事情。

在树莓派上安装 Java SE 8

在撰写本文时,Raspbian Wheezy 预装了 JDK 8。要检查,只需在 Pi 命令提示符下输入以下内容:

pi@raspberrypi ~ $ java –version

你会看到类似于这样的东西,取决于当前安装和可访问的版本:

在树莓派上安装 Java SE 8

树莓派 Wheezy 上的 Java 版本

重要的是第二行:如果它不是 1.8.n,你需要安装 JDK8。

安装 Java SE 8

我们已经在之前安装了我们的 JDK 8,并且所有必要的步骤都在第一章的安装 Java SE 8 JDK部分中描述。

添加 JavaFX

如前所述,Oracle 已经撤销了对 JavaFX 嵌入式的支持。如果你安装了 JDK 8u45 或预装在 Raspbian Wheezy 上的版本,没有jfxrt.jar捆绑,所以我们需要提供它以便在我们的 Pi 上运行 JavaFX 应用程序。

一种方法是按照wiki.openjdk.java.net/display/OpenJFX/Cross+Building+for+ARM+Hard+Float上的教程,为 ARM 交叉构建 OpenJFX。这是给非常高级的开发者。

一个更简单的方法是下载一个预构建的发行版,比如托管在 JavaFXPorts 项目上的armv6hf-sdk.zipbitbucket.org/javafxports/arm/downloads)。

一旦你下载了armv6hf-sdk.zip,解压它并添加这个命令行选项,将外部源附加到classpath上,使用扩展机制:

-Djava.ext.dirs=<path to armv6hf-sdk>/rt/lib/ext

或者,你可以将这个 zip 文件中rt/lib/extrt/lib/arm的内容复制到你的 JVM 文件夹中,避免使用扩展机制。

为树莓派配置 NetBeans

NetBeans 8 增加了指向远程 JDK 并使用它来远程调试和执行你在本地开发机器上编写的程序的能力。它甚至可以自动无缝地部署你的应用程序。正如 José Pereda 在他的文章netbeans.dzone.com/articles/nb-8-raspberry-pi-end2end中所记录的,你可以通过以下步骤启用这个功能。

  1. 在你的机器上启动 NetBeans。

  2. 从菜单栏选择工具,然后选择Java 平台。点击添加平台按钮。

  3. 选择远程 Java 标准版单选按钮,然后点击下一步

  4. 提供以下条目(如下截图所示):

平台名称JavaFX on Raspberry Pi JDK 8

主机:输入你之前分配的树莓派的静态 IP 地址或主机名

用户名pi

密码raspberry

远程 JRE 路径/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/jre

为树莓派配置 NetBeans

为 Pi 设置远程平台

  1. 点击完成按钮,等待 NetBeans 建立和配置远程 JDK 连接。

  2. 一旦远程 JDK 就位,点击关闭按钮。

现在我们已经完成了设置,你应该拥有一个开发环境,它是为开发 JavaFX 8 应用程序为树莓派提供的最好的之一。那么让我们开始吧!

开关应用程序

开关应用程序在其本质上非常简单,但主要分为两个要点:如何在树莓派上运行 JavaFX 8 应用程序,以及如何从树莓派的通用输入/输出GPIO)控制外部世界。我们将使用一个名为Pi4j的项目来实现这个目的。

这个想法很简单;我们将创建一个 JavaFX 应用程序,它将充当一个开关控制器,用于控制连接到你的树莓派的电路上的 LED。

以下截图显示了应用程序处于开启和关闭状态:

开关应用程序

开启应用程序开关状态

为树莓派配置 NetBeans

开关应用程序

关闭应用程序开关状态

什么是 Pi4J 库?

Pi4j 库(pi4j.com)是一个旨在提供本地库和 Java 之间的桥梁,以完全访问树莓派功能和控制的项目,因此你可以轻松地访问 GPIO 引脚用于你的 Java 项目。

访问pi4j.com/pins/model-2b-rev1.html查看树莓派 2 型 B(J8 头)的 GPIO 引脚编号。此外,你的套件的 GPIO 适配器可能附带 GPIO 引脚头的快速参考。

对于这个例子,你将需要一些基本的电子元件,比如 LED、电阻和面包板。如果你的套件中没有包括这些,你可以从网上商店购买。

电路设置

现在我们需要通过在面包板上添加一个带有 220 欧姆上拉电阻的 LED 来设置我们的电路,并将阳极连接到 GPIO 引脚#1,阴极连接到 GPIO GND 引脚,如下图所示(CanaKit 附带了一个常用电子零件的通用组装指南):

电路设置

开关器应用电路设置

应用程序

如前所述,应用程序 UI 包含两个按钮。退出!负责关闭 GPIO 控制器并关闭应用程序。第二个按钮是一个切换按钮(/),可以作为开关使用。它有两种状态:选中时,其状态为 true,当未选中时,其状态为 false。此外,我们通过编程方式更改其标签,以指示当前受控 LED 的状态。

此外,还有一个圆形形状,模仿了物理 LED 状态。因此,当切换按钮打开时,圆圈将填充为红色。关闭时,它变成黑色,这是默认状态。

最后,在应用程序场景的底部,我们添加一个名为 Pi 信息的TitledPane,显示一些树莓派信息。

通过查看SwitchUIController.java类,您会发现在与Pi4J库交互之前,我们有一些非常重要的字段要声明:

private GpioController gpio;
private GpioPinDigitalOutput pin;

第一行负责创建一个新的 GPIO 控制器实例,这是通过initialize()方法通过GpioFactory完成的,因为它包括一个createInstance方法来创建 GPIO 控制器:

gpio = GpioFactory.getInstance();

注意

您的项目应该只实例化一个 GPIO 控制器实例,并且该实例应该在整个项目中共享。

要访问 GPIO 引脚,必须首先配置引脚。配置根据您打算如何使用它来配置引脚。配置可以自动导出引脚,设置其方向,并为基于中断的事件设置任何边缘检测:

// provision gpio pin #01 as an output pin and turn on
pin = gpio.provisionDigitalOutputPin(GPIO_01);

这是如何配置输出引脚#1。您的程序将只能控制那些配置为输出引脚的引脚的状态。输出引脚用于控制继电器、LED 和晶体管。

现在我们想要做的就是使用切换按钮从我们的应用程序控制 LED。这是通过注册到切换按钮的doOnOff()事件函数来完成的,如下面的代码所示:

    @FXML
    private void doOnOff(ActionEvent event) {
        if (switchTgl.isSelected()) {
            pin.high();
            led.setFill(RED);
            switchTgl.setText("OFF");
            System.out.println("Switch is On");
        } else {
            pin.low();
            led.setFill(BLACK);
            switchTgl.setText("ON");
            System.out.println("Switch is Off");
        }
    }

P14J库提供了许多方便的方法来控制或向 GPIO 引脚写入状态。在我们的应用程序中,我们使用pin.high()来打开 LED,使用pin.low()来关闭 LED。

最后,当应用程序退出时,我们必须关闭 GPIO 控制器。Pi4J 项目提供了一个实现,可以在应用程序终止时自动将 GPIO 引脚状态设置为非活动状态。

这对于确保 GPIO 引脚状态在程序关闭时不活动或保持某些活动是有用的。我们可以简单地使用我们之前创建的 GPIO 实例的以下代码行来实现这一点:

gpio.shutdown();

当您按下切换按钮以打开 LED 时,您会看到绿色 LED 发光。当它关闭时,您会看到 LED 变暗。

应用程序

应用电路-LED 关闭

应用程序

应用电路-LED 开启

接下来,让我们配置我们的项目,从 NetBeans 直接在树莓派上运行我们的 JavaFX 开关应用程序。

在 NetBeans 中使用 Pi

在讨论了我们的应用程序逻辑并了解了它的工作原理之后,现在是最好的部分:使用 NetBeans 构建您的应用程序并在树莓派上运行它。步骤如下:

  1. 在 NetBeans 的项目选项卡中右键单击Chapter6项目,然后选择属性

  2. 项目属性框中,从左侧的类别菜单中选择运行。您将看到一个类似于以下截图的对话框:在 NetBeans 中使用 Pi

项目属性对话框和运行实例

  1. 单击所选“配置”右侧的“新建”按钮。为“新配置”(Pi Remote Config)设置一个名称,然后单击“确定”按钮,如下面的屏幕截图所示:使用 NetBeans 与 Pi

新配置

  1. 现在您必须将远程 JDK 与远程配置关联起来。要这样做,单击标记为“运行平台”的组合框,并选择您之前配置的JavaFX on Raspberry Pi JDK 8。不要忘记在“VM 选项”中添加jfxrt.jar的路径:使用 NetBeans 与 Pi

远程 Pi JDK 关联

  1. 最后一步是构建并部署应用程序到树莓派。要这样做,转到“运行”菜单,选择“运行项目”,并观看 NetBeans 输出窗口/选项卡。在运行应用程序时,如果您留意 Pi 的屏幕,您将看到以下输出消息:
jfx-deployment-script:
jfx-deployment:
jar:
Connecting to 192.168.2.150:22
cmd : mkdir -p '/home/pi/NetBeansProjects/Chapter6/dist'
Connecting to 192.168.2.150:22
done.
profile-rp-calibrate-passwd:
Connecting to 192.168.2.150:22
cmd : cd '/home/pi/NetBeansProjects/Chapter6';
'/usr/lib/jvm/jdk-8-oracle-arm-vfp-hflt/jre/bin/java'  -Dfile.encoding=UTF-8 -jar /home/pi/NetBeansProjects/Chapter6/dist/Chapter6.jar

摘要

在本章中,我们将信用卡大小的微型计算机(不大于一副扑克牌)转变为一个 JavaFX 开关控制器机。在此过程中,您学会了关于树莓派的知识,如何创建可引导的 SD 卡,如何将操作系统安装到其中,如何为其配置最佳性能和远程连接性,如何为其分配固定的网络(IP)地址,以及如何从开发机连接到 Pi。

我们还重新讨论了如何在树莓派和开发机上安装 JDK 8/ JavaFX 8,并学会了如何在开发机上安装和配置 NetBeans,以便它可以使用 Pi 上的 JDK 作为远程平台进行调试和执行。

在您的开发机和树莓派都准备就绪后,我们讨论了使用 JavaFX 和一些选择的工具/ API(包括 Pi4j)开发一个简单但很棒的应用程序来控制树莓派外部世界所需的原则。

最后,您学会了如何从 NetBeans 远程部署应用程序到树莓派,只需点击几下即可。

在下一章中,我们将监控一个温度传感器,从 Arduino 板上测量您的血液有多热。

第七章:使用 JavaFX 监控和控制 Arduino

Arduino是一种基于简单可编程微控制器板的开源电子工具,可以使用免费开源 IDE 进行编程。单独或连接到计算机,它可以创建可以通过从各种开关或传感器获取输入来感知,并可以通过控制各种灯、电机和其他输出物理设备来执行的交互式设备。

作为第一批物联网IoT)设备之一,它是在 2005 年创建的。它从物联网概念的最初阶段就存在。

Arduino 可以独立运行,也可以与计算机上运行的软件(Java、JavaFX、Python 等)进行通信,板可以手工组装或购买预装的。

事实上,Arduino 简化了与微控制器的工作过程。对于教师、学生和有兴趣的业余爱好者来说,它比其他系统更具吸引力,因为它价格低廉——Arduino 板的成本低于 50 美元。

简单、清晰、易于使用的编程环境;开源和可扩展的软件;以及开源和可扩展的硬件等功能,使 Arduino 支持自己动手和与他人一起动手的概念,这是制造运动的定义。

本章将向您展示如何使用 JavaFX 开发桌面应用程序以及 Arduino 板,以监视来自真实世界温度传感器的数据,并在图表上报告它,“你到底有多热血!”

在本章中,您将:

  • 熟悉 Arduino 板及其组件

  • 安装和准备 Arduino 软件和环境、IDE 和驱动程序

  • 开发 Arduino 血糖仪草图以控制和监视电路

  • 使用串行通信将 Arduino 数据读入 JavaFX 应用程序

  • 使用 JavaFX 图表 API 呈现数据

Arduino 板是什么?

Arduino Uno 是最知名的 Arduino 板,是基于 ATmega328 数据表(www.atmel.com/dyn/resources/prod_documents/doc8161.pdf)的微控制器板,这是板的大脑。它大约 3 x 2 英寸大小。它有 14 个数字输入/输出引脚,6 个模拟输入引脚和 32 千字节的闪存内存。

每个板都有一个复位按钮。此外,它包括一个 USB 端口,因此当连接到计算机时,它成为电源和通信工具。如果未连接到计算机,可以使用备用电源,例如 AC 9 至 12V DC 适配器,可以通过将 2.1 毫米中心正极插头插入板的电源插孔,或 9V 电池包连接。

带有波浪符号的六个数字引脚旁边的引脚是允许脉宽调制PWM)的引脚,这是一种用于控制电源并在数字输入引脚上模拟模拟信号的技术。使用这些引脚的原因之一可能是控制 LED 的亮度。

Arduino Uno 的官方规格可以在arduino.cc网站的arduino.cc/en/Main/ArduinoBoardUno上找到。访问www.arduino.cc/en/Main/Products以获取有关其他 Arduino 板的信息,例如 Mega,Due 或 Yun,以及下一个发布的 Tre 和 Zero。

以下图片显示了 Arduino Uno R3 板:

什么是 Arduino 板?什么是 Arduino 板?

你可以用它做什么?

您的 Arduino 板可能很小,但不要让它的大小欺骗您。它功能强大,有很大的发展空间。它特别强大,因为它是建立在开放硬件和开放软件平台上的。我们不会花时间讨论开源;简而言之,这意味着有关硬件和软件的信息是免费提供的,易于找到。

Arduino 可以用来通过接收输入来感知环境。它也可以控制输出,如灯、电机、传感器等。

你可以使用开源的 Arduino 编程语言对板上的微控制器进行编程。

相关网站和文档

开源和开放硬件平台的一个很大的优势是你可以在互联网上找到信息。

寻找关于 Arduino 信息的好地方是官方页面:arduino.cc网站的arduino.cc/en/Guide/HomePage。随着你的技能增长,你会想要研究更高级的主题,知道在哪里找到答案会很有帮助。

另一个很棒的网站是adafruit.com。这个网站有教程、示例、有用的论坛,还有一个可以购买你需要的零件的商店。

对于孩子们来说,另一个有趣的应用是将乐高 Mindstorm传感器和电机与 Arduino 结合使用。我推荐网站wayneandlayne.com,因为它一直是我整合乐高和 Arduino 的灵感和起点。如果你正在寻找零件和项目,这是一个很好的网站。

设置你的 Arduino

如果这是你第一次接触 Arduino,我强烈建议你从套件开始,而不是组装所有的单个组件。

本章中的大部分活动都可以使用来自 arduino.cc 的 Arduino 入门套件完成,如下图所示。它包括了 Arduino Uno R3 和其他组件,可以完成大部分预先打包的项目。有关套件的完整描述,请访问store.arduino.cc/product/K000007

设置你的 Arduino

Arduino 入门套件(包括零件、板和项目书)

购买 Arduino

虽然 Arduino Uno 的成本约为 25 美元,但你可以购买不同的套件,包括板,从基本的 Budget Pack(50 美元)到 Arduino Starter Pack(65 美元)的adafruit.com,或者从arduino.cc购买 Starter Kit(90 美元)。这些套件与 Budget Pack 具有相同的组件,但它们还包括一些额外的高级调试工具。

arduino.cc的入门套件中获得的一个很好的优势是,它包括了一个指导书,其中包括了 15 个不同难度的项目。

如果你是亚马逊用户,通常可以在他们的网站上找到相同的套件,但价格可能会有所不同。

大多数板的核心组件位置相同。因此,更高级的板已经加长以容纳额外的组件。

以下是一些购买零件和书籍的网站:arduino.cc, Adafruit.com, makershed.com, sparkfun.com, 和 Amazon.com

你将需要的其他组件

除了 Arduino,你还需要一台带有 Windows、Mac OS 或 Linux 的计算机,带有 USB 端口,用于将计算机连接到板上。

对于血糖仪项目,你将需要一些已经包含在 Arduino 入门套件中的零件。以下是你应该准备好的零件的简短清单。

一台带有 USB 端口的计算机,一根 USB 电缆,一个无焊面包板,柔性导线,一个 TMP36 温度传感器,三个 220 欧姆电阻,和三个 LED 灯(黄色、蓝色和红色),如下图所示:

你将需要的其他组件

血糖仪项目的工具和材料

Arduino 集成开发环境

为了与 Arduino 微控制器进行交互和编程,我们需要下载并安装 Arduino 集成开发环境。

Arduino 软件包括您编写代码所需的所有组件,文本编辑器和编译器将其转换为机器语言,并将其上传到您的板并运行代码。

下载 IDE

在撰写本文时,Arduino IDE 版本为 1.6.3,但您可以从链接www.arduino.cc/en/Main/Software获取 Arduino 软件的最新版本。除了以下截图中显示的 Arduino 版本外,还要单击首选操作系统链接;在我的情况下,我选择了 Mac OS X。

从捐赠页面,要么捐赠,要么只需单击JUST DOWNLOAD链接即可开始下载 IDE;在我的情况下,我选择了arduino-1.6.4-macosx.zip

下载后,解压文件并将Arduino.app文件复制到 Mac 上的应用程序文件夹,或者将 Arduino 可执行文件链接到您方便访问的位置。

一旦您下载了 IDE,您仍然需要解决一些硬件问题,然后才能开始编程。

下载 IDE

下载 Arduino IDE 1.6.4

安装驱动程序

首先,您需要使用 USB 电缆将 Arduino 板连接到计算机。绿色 LED 电源指示灯(标有 PWR 或 ON)应亮起。

Windows 设置

让我们在 Windows 中设置 Arduino:

  1. 插入您的板,并等待 Windows 开始其驱动程序安装过程。

  2. 单击开始菜单,然后打开控制面板

  3. 控制面板,导航到系统和安全。接下来,单击系统。打开系统窗口后,选择设备管理器

  4. 查看端口(COM 和 LPT)。您应该看到一个名为Arduino UNO(COMxx)的开放端口。如果没有COM 和 LPT部分,请在其他设备下查找未知设备

  5. 右键单击Arduino UNO(COMxx)端口,然后选择更新驱动程序软件选项。

  6. 接下来选择浏览我的计算机以查找驱动程序软件选项。

  7. 最后,导航并选择名为arduino.inf的驱动程序文件,该文件位于 Arduino 软件下载的Drivers文件夹中(而不是FTDI USB Drivers子目录)。

  8. Windows 将完成驱动程序安装。

提示

如果您使用的是 Windows 8,驱动程序安装不完整,请尝试禁用驱动程序签名强制执行。

Mac OS X 和 Linux 设置

对于 Mac OS X 和 Linux 操作系统,不需要安装驱动程序。

对于 Mac OS X,当您连接 Arduino 板时,您应该在/dev/tty.usbmodemXXXX 或/dev/tty.usbserialXXXX下看到它列出。

在 Linux 上,当您连接 Arduino 板时,您应该在/dev/ttyACMX 或/dev/ttyUSBX下看到它列出。

探索 IDE 和草图

假设您的安装成功结束,双击 Arduino 应用程序,您应该看到以下屏幕:

探索 IDE 和草图

Arduino IDE,首次运行为空草图

现在,您需要做两件重要的事情,以便正确连接和上传草图到 Arduino 板。首先,通过导航到工具 | 来选择您的板。然后,通过转到工具 | 串行端口来选择 Arduino 板的串行端口。

最后的验证步骤是运行 Arduino 的Hello world,您可以通过在文件 | 示例 | 1.Basics | Blink中打开 LED 闪烁示例草图来实现。

现在,只需在环境中单击上传按钮。如果上传成功,状态栏中将出现消息上传完成

等待几秒钟,您将看到板上的RXTX LED 闪烁。

如果您遇到任何问题,请查看arduino.cc/en/Guide/Troubleshooting上的故障排除建议。

恭喜,您的 Arduino 已经启动运行!

血糖仪项目

在这个项目中,我们将使用温度传感器来测量你的皮肤温度,然后根据温度来开启(或关闭)LED 灯。

首先,我们将调整我们的板子,并准备好使用其他你需要的组件部分中描述的组件进行项目。然后,我们将编写草图来读取传感器数据,并根据你的皮肤温度的数据,来开启和关闭 LED 灯。

最后,我们将用温度传感器数据来供给我们的 JavaFX 应用,并使用图表 API 显示结果,以指示你的皮肤温度水平。

调整电路

现在,我们将调整我们的血糖仪电路,如下图所示。首先,通过连接跳线线将 Arduino UNO 和面包板连接起来。我已经将 TMP36 温度传感器连接到了面包板上,所以传感器的圆形部分远离 Arduino。引脚的顺序非常重要!请注意,我们已经将左边的引脚连接到电源,右边的引脚接地,中间输出电压的引脚连接到板子上的模拟引脚 A0。如下图所示:

调整电路

血糖仪示例的电路布局

最后,我已经连接了三个 LED 灯和电阻,并将它们连接到数字 PMW~引脚排的 Arduino 引脚 4、~3 和 2。

像往常一样,我已经将面包板的+行连接到电源(5V),-行连接到地(GND)。

注意

记得在设置组件时保持板子未插电。

草图

在我们调整了电路并配置了一切之后,我们需要对微控制器进行编程。这就是草图将发挥作用的地方:

/*
  Chapter 7 example
  Project  - Blood-Meter

  This sketch is written to accompany Project in the
  JavaFX 8 essentials book

  Parts required:
  1 TMP36 temperature sensor
  3 red LEDs
  3 220 ohm resistors

  Created 5 April 2015
  by Mohamed Mahmoud Taman
  */

// named constant for the pin the sensor is connected to
const int sensorPin = A0;
// Room temperature in Celsius
const float baselineTemp = 25.0;

void setup() {
  // open a serial connection to display values
  Serial.begin(9600);
  // set the LED pins as outputs
  // the for() loop saves some extra coding
  for (int pinNumber = 2; pinNumber < 5; pinNumber++) {
    pinMode(pinNumber, OUTPUT);
    digitalWrite(pinNumber, LOW);
  }
}

void loop() {
  // read the value on AnalogIn pin 0
  // and store it in a variable
  int sensorVal = analogRead(sensorPin);

  // send the 10-bit sensor value out the serial port
  Serial.print("Sensor Value: ");
  Serial.print(sensorVal);

  // convert the ADC reading to voltage
  float voltage = (sensorVal / 1024.0) * 5.0;

  // Send the voltage level out the Serial port
  Serial.print(", Volts: ");
  Serial.print(voltage);

  // convert the voltage to temperature in degrees C
  // the sensor changes 10 mV per degree
  // the datasheet says there's a 500 mV offset
  // ((voltage - 500mV) times 100)
  Serial.print(", degrees C: ");
  float temperature = (voltage - .5) * 100;
  Serial.println(temperature);

  // if the current temperature is lower than the baseline
  // turn off all LEDs
  if (temperature < baselineTemp) {
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);
    digitalWrite(4, LOW);
  } // if the temperature rises 2-4 degrees, turn an LED on
  else if (temperature >= baselineTemp + 2 && temperature < baselineTemp + 4) {
    digitalWrite(2, HIGH);
    digitalWrite(3, LOW);
    digitalWrite(4, LOW);
  } // if the temperature rises 4-6 degrees, turn a second LED on
  else if (temperature >= baselineTemp + 4 && temperature < baselineTemp + 6) {
    digitalWrite(2, HIGH);
    digitalWrite(3, HIGH);
    digitalWrite(4, LOW);
  } // if the temperature rises more than 6 degrees, turn all LEDs on
  else if (temperature >= baselineTemp + 6) {
    digitalWrite(2, HIGH);
    digitalWrite(3, HIGH);
    digitalWrite(4, HIGH);
  }
  delay(100);
}

工作原理

如果你阅读每行的注释,你会理解代码。不深入细节,以下是草图的主要要点。

每个 Arduino 草图都有两个主要的方法:setup()loop()。第一个方法用于初始化引脚为输入或输出,打开串行端口,设置它们的速度等。第二个方法在微控制器内部重复执行任务。

一开始,我们有一对有用的常量:一个引用模拟输入,另一个保存基准温度。对于每2 度高于这个基准温度,一个 LED 将打开。

setup()方法中,我们将串行端口初始化为每秒 9,600 位的速度,并使用for循环将一些引脚设置为方向(输出引脚)并关闭它们。

loop()方法中,我们开始读取温度传感器的电压数值,范围在 0 到 1,023 之间,然后使用Serial.print()将传感器数值发送到串行端口,以便任何连接的设备(例如我们的计算机)可以读取。这些模拟读数可以测量房间的温度或者如果你触摸传感器的话,也可以测量你的皮肤温度。

我们需要使用以下方程将模拟传感器读数转换为电压值:

voltage = (sensorVal / 1024.0) * 5.0

从数据表中,我们使用传感器规格将电压转换为温度的方程:

temperature = (voltage - .5) * 100

根据实际温度,你可以设置一个if else语句来点亮 LED 灯。使用基准温度作为起点,每增加 2 度温度,你将打开一个 LED 灯。

当你在温度刻度上移动时,你会寻找一系列数值。

模拟到数字转换器ADC)读取速度非常快(以微秒为单位),建议在loop()函数的末尾设置 1 毫秒的延迟。但考虑到这将被发送到串行端口,最终设置了 100 毫秒的延迟。

测试、验证并将草图上传到 Arduino

将代码上传到 Arduino 后,点击串行监视器图标,如下图所示:

测试、验证并将草图上传到 Arduino

Arduino IDE 工具栏图标

您应该看到一系列数值以如下格式输出:

Sensor Value: 158, Volts: 0.77, degrees C: 27.15

现在尝试在传感器插入面包板时用手指触摸传感器周围,看看串行监视器中的数值会发生什么变化。

在传感器放在空气中时记下温度。关闭串行监视器并将 baselineTemp 常量更改为您之前观察到的值。再次上传代码并尝试再次握住传感器;随着温度的升高,您应该看到 LED 逐个打开。

恭喜,热门!

从串行端口读取数据

在 Java 中没有标准的方法来读取串行端口,因为这是一个硬件特定的任务,违反了 Java 的多平台概念。因此,我们需要一个第三方库来完成这项任务,并且它应该是用 Java 编写的,以便与我们的应用程序集成。

Arduino IDE 使用了第一个串行通信库,称为 RXTX。最初来自 Trent Jarvi,并在 LGPL v2.1+ Linking Over Controlled Interface 许可下分发,直到 1.5.5 beta 版本与板通信。然而,它非常慢,现在已经不推荐使用。

新的 Java Simple Serial Connector (jSSC) 库由 Alexey Sokolov 开发,根据 GNU Lesser GPL 许可。自 1.5.6 beta 版本以来,Arduino IDE 使用新库进行板通信,因为它比之前的版本更快。

该库的另一个重要优势是,它作为单个 jssc.jar 文件进行分发,其中包括所有平台的本地接口,以减少每个平台和操作系统的本地安装的痛苦。它会在运行时将它们添加到 classpath 中,如下截图所示:

从串行端口读取数据

jSSC 2.8.0 本地库

您可以从github.com/scream3r/java-simple-serial-connector/releases下载最新版本。在撰写本文时,jSSC 版本为 2.8.0。

JavaFX 血糖监测应用程序

我们将设计一个 JavaFX 8 应用程序,该应用程序从温度传感器中获取读数,并在折线图中显示数值。我们还将展示模拟板 LED 的一组形状的变化。为了清晰起见,我们将使用两个类,一个用于串行读数,另一个用于 JavaFX UI 和主应用程序 BloodMeterFX 文件,包括图表 API。

我们将使用一个包含从串行端口读取的最后一行的 StringProperty 来绑定这些类(Serial 和 BloodMeterFX)。通过在 JavaFX 线程中监听此属性的更改,我们将知道何时有新的读数要添加到图表中。

完整的项目代码可以从 Packt Publishing 网站下载。

Java 中的串行通信

让我们首先解释 Serial.java 类。这个类的代码大部分来自 JavaFX 8 Introduction By Example, Apress,但核心读取函数有所改变,如下面的代码片段所示:

您应该将 jSSC.jar 文件包含到您的类路径中,可以通过将其添加到 Linux 或 Windows 的 <JAVA_HOME>/jre/lib/ext(或 Mac 上的 /Library/Java/Extensions)中,或者更好地将其添加到您的项目库中,如前面的截图所示,如果您打算分发您的应用程序。

为了能够读取串口,我们需要导入以下 jSSC 类:

import jssc.SerialPort;
import static jssc.SerialPort.*;
import jssc.SerialPortException;
import jssc.SerialPortList;

为了动态读取端口,如果您不知道通过这个类的构造函数设置的确切端口名称,我们有一组端口名称可帮助您选择 Arduino 板可以连接到的适当端口。

private static final List<String> USUAL_PORTS = Arrays.asList(
  "/dev/tty.usbmodem", "/dev/tty.usbserial", //Mac OS X
  "/dev/usbdev", "/dev/ttyUSB", "/dev/ttyACM", "/dev/serial", //Linux
  "COM3", "COM4", "COM5", "COM6" //Windows
);

private final String ardPort;

public Serial() {
      ardPort = "";
}

public Serial(String port) {
      ardPort = port;
}

connect()方法会查找一个有效的串行端口,如果没有连接到 Arduino 板,则会设置一个。如果找到了有效的串行端口,就会打开它并添加一个监听器。这个监听器负责每次从 Arduino 输出返回一行时获取输入读数。stringProperty会被设置为这一行。我们使用StringBuilder来存储字符,并在找到'\r\n'时提取行内容。我们在这里使用了 lambda 表达式提供的集合批量操作,以便简单地查找端口列表并根据操作系统返回有效的端口。

通过set()方法将找到的每一行设置为line变量,以便通过注册的 change 监听器事件对line变量进行必要的更改,这通过getLine()方法暴露出来。代码如下:

public boolean connect() {
  out.println("Serial port is openning now...");
  Arrays.asList(SerialPortList.getPortNames()).stream()
  .filter(name -> ((!ardPort.isEmpty() && name.equals(ardPort))|| (ardPort.isEmpty() && USUAL_PORTS.stream()
  .anyMatch(p -> name.startsWith(p)))))
  .findFirst()
  .ifPresent(name -> {
  try {
    serPort = new SerialPort(name);
      out.println("Connecting to " + serPort.getPortName());
      if (serPort.openPort()) {
        serPort.setParams(BAUDRATE_9600,
        DATABITS_8,
        STOPBITS_1,
        PARITY_NONE);
        serPort.setEventsMask(MASK_RXCHAR);
        serPort.addEventListener(event -> {
         if (event.isRXCHAR()) {
           try {
             sb.append(serPort.readString(event.getEventValue()));
             String ch = sb.toString();
             if (ch.endsWith("\r\n")) {
               line.set(ch.substring(0, ch.indexOf("\r\n")));
               sb = new StringBuilder();
             }
           } catch (SerialPortException e) {
             out.println("SerialEvent error:" + e.toString());
           }
         }
       });
     }
  } catch (SerialPortException ex) {
    out.println("ERROR: Port '" + name + "': " + ex.toString());
  }});
  return serPort != null;
}

最后,disconnect()方法负责从端口中移除监听器并关闭端口连接,以释放应用程序使用的资源。代码如下:

public void disconnect() {
  if (serPort != null) {
    try {
      serPort.removeEventListener();
      if (serPort.isOpened()) {
        serPort.closePort();
      }
      } catch (SerialPortException ex) {
      out.println("ERROR closing port exception: " + ex.toString());
    }
    out.println("Disconnecting: comm port closed.");
  }
}

应用程序逻辑和图表 API

我们应用程序的主要组件是LineChart<Number, Number>图表类 API,它将用于在 Y 轴上绘制您的血温水平,而在 X 轴上绘制时间。

自 JavaFX 2 以来,具有两个轴(如线条、条形和区域图表)的图表已经可用,并且它们是Node类的类型,这使得将它们添加到Scene中像其他节点一样变得容易。

在我们的应用程序中,我们将添加以下createBloodChart()方法,它负责创建和准备图表,并将其返回以添加到主应用程序场景中。

在应用程序的开始,我们有实例变量:一个Serial对象来处理 Arduino 的连接和读数;listener用于注册到Serial线对象;BooleanProperty用于跟踪连接状态;以及三个浮点属性,分别用于跟踪所有传感器数据的实际值、电压转换,最后是将电压转换为摄氏度温度。代码如下:

private final Serial serial = new Serial();
private ChangeListener<String> listener;
private final BooleanProperty connection = new SimpleBooleanProperty(false);
private final FloatProperty bloodTemp = new SimpleFloatProperty(0);
private final FloatProperty volts = new SimpleFloatProperty(0);
private final FloatProperty sensorVal = new SimpleFloatProperty(0);

我们将添加LineChart来绘制温度传感器的温度水平,其中有一个Series,它接受一对数字来绘制在每个轴上;这些是NumberAxis实例。XYChart.Data被添加到系列数据中,作为每个点的XY值对来绘制读数。

每当Series的大小大于 40 个点时,为了内存效率,将删除前面的值。代码如下:

private LineChart<Number, Number> createBloodChart() {
  final NumberAxis xAxis = new NumberAxis();
  xAxis.setLabel("Temperature Time");
  xAxis.setAutoRanging(true);
  xAxis.setForceZeroInRange(false);
  xAxis.setTickLabelFormatter(new StringConverter<Number>() {
    @Override
    public String toString(Number t) {
      return new SimpleDateFormat("HH:mm:ss").format(new Date(t.longValue()));
    }
    @Override
    public Number fromString(String string) {
      throw new UnsupportedOperationException("Not supported yet.");
    }
  });
  final NumberAxis yAxis = new NumberAxis("Temperature value", baselineTemp - 10, 40.0, 10);
  final LineChart<Number, Number> bc = new LineChart<>(xAxis, yAxis);
  bc.setTitle("Blood temperature vs time");
  bc.setLegendVisible(false);

  Series series = new Series();
  series.getData().add(new Data(currentTimeMillis(), baselineTemp));
  bc.getData().add(series);

  listener = (ov, t, t1) -> {
    runLater(() -> {
      String[] values = t1.split(",");
      if (values.length == 3) {
        sensorVal.set(parseFloat(values[0].split(":")[1].trim()));
        volts.set(parseFloat(values[1].split(":")[1].trim()));
        bloodTemp.set(parseFloat(values[2].split(":")[1].trim()));
        series.getData().add(new Data(currentTimeMillis(),
        bloodTemp.getValue()));

        if (series.getData().size() > 40) {
          series.getData().remove(0);
        }
      }

    });
  };
  serial.getLine().addListener(listener);

  return bc;
}

这里最有趣的部分是使用 lambda 表达式创建的 change 监听器listener = (ov, t, t1) -> {},它将被注册到我们之前描述的Serialline对象上。通过这样做,我们能够在检测到 Arduino 的任何输入时改变图表数据。

为此,我们将X坐标值设置为添加读数的毫秒时间(在图表上,它将被格式化为HH:MM:SS),Y坐标值是 Arduino 报告的温度级别的浮点测量值在字符串t1中。

注意

Platform.runLater()的主要用途是将填充系列数据的任务放在 JavaFX 线程中,但它也为Scene图形提供了所需的时间来渲染图表,如果值添加得太快,则会跳过值。

我添加了四个Circle类型的形状,它们将用于根据温度水平模拟电路 LED 的开和关,一旦通过 change 监听器对FloatProperty bloodTemp进行了任何更改。代码如下:

Circle IndicatorLevel1 = new Circle(26.0, Color.BLACK);
bloodTemp.addListener((ol, ov, nv) -> {
  tempLbl.setText("Degrees C: ".concat(nv.toString()));

  // if the current temperature is lower than the baseline turn off all LEDs
  if (nv.floatValue() < baselineTemp +2) {
    IndictorLevel1.setFill(Paint.valueOf("Black"));
    IndictorLevel2.setFill(Paint.valueOf("Black"));
    IndictorLevel3.setFill(Paint.valueOf("Black"));
  } // if the temperature rises 1-3 degrees, turn an LED on
  else if (nv.floatValue() >= baselineTemp + 1 && nv.floatValue()< baselineTemp + 3) {
      IndictorLevel1.setFill(Paint.valueOf("RED"));
      IndictorLevel2.setFill(Paint.valueOf("Black"));
      IndictorLevel3.setFill(Paint.valueOf("Black"));
    } // if the temperature rises 3-5 degrees, turn a second LED on
    else if (nv.floatValue() >= baselineTemp + 4 && nv.floatValue() < baselineTemp + 6) {
      IndictorLevel1.setFill(Paint.valueOf("RED"));
      IndictorLevel2.setFill(Paint.valueOf("RED"));
      IndictorLevel3.setFill(Paint.valueOf("Black"));
    }//if the temperature rises more than 6 degrees, turn all LEDs on
    else if (nv.floatValue() >= baselineTemp + 6 {
    IndictorLevel1.setFill(Paint.valueOf("RED"));
    IndictorLevel2.setFill(Paint.valueOf("RED"));
    IndictorLevel3.setFill(Paint.valueOf("RED"));
  }
});

最后,主 UI 是由loadMainUI()方法创建的,它负责创建整个 UI 并将所有必需的变量绑定到 UI 控件,以便动态地与来自 Arduino 输入的事件交互。

一旦场景根(BorderPane)对象通过loadMainUI()准备和设置好,我们就创建场景并将其添加到舞台中,以便运行我们的应用程序如下:

Scene scene = new Scene(loadMainUI(), 660, 510);
stage.setTitle("Blood Meter v1.0");
stage.setScene(scene);
stage.show();
//Connect to Arduino port and start listening
connectArduino();

最后,从Application类继承的重写的stop()方法将通过关闭Serial端口连接和从线对象中移除listener来处理任何资源释放。代码如下:

@Override
public void stop() {
  System.out.println("Serial port is closing now...");
  serial.getLine().removeListener(listener);
  if (connection.get()) {
  serial.disconnect();
  connection.set(false);
}}

运行应用程序

当一切就绪时——具有早期描述的类和添加到其中的jSSC.jar库的 JavaFX 项目——编译并运行您的应用程序,同时您的 Arduino 板连接到您的笔记本电脑/PC。如果一切正常,您将看到以下截图,显示了图表上的温度值与时间值,这将基于您的室温。

恭喜,您现在正在监视 Arduino 输入,并且可以通过jSSC.jar库与 Arduino 进行交互控制。

运行应用程序

初始血液计应用读数,温度为 24.71 度

尝试用手指拿住传感器并监视图表上的读数。在我的情况下,它达到了 30.57 度。还要注意工具栏上的指示器水平和板上的 LED。您应该看到类似于以下截图:

运行应用程序

血液计应用读数,温度为 30.57 度

总结

在本章中,您了解了通过结合 Arduino 和 JavaFX 可以实现什么。您首先学习了关于 Arduino 的知识,它的不同板,主要规格,购买地点和组件。然后,我们讨论了更多项目灵感的网站。

接下来,您学会了如何下载和设置 Arduino IDE 与 Arduino 通信。在了解了如何在 IDE 中加载示例之后,您有机会尝试自己动手,通过使用 Arduino Uno 和温度传感器构建一个简单的电路来创建一个草图,并在 IDE 串行监视器中读取温度水平。

然后,您学会了如何使用 Java Simple Serial Connector 库从计算机读取串行端口数据。

您学会了如何使用 JavaFX Line Chart API 来监视和显示 Arduino 读数。然后,您看到了一个示例,其中使用 Arduino 板从串行端口绘制了一个 JavaFX 图表,使用温度传感器来测量血液温度水平。

在下一章中,您将学习如何通过手势控制您的 JavaFX 应用程序与计算机进行无触控交互,而无需任何输入设备,如键盘、鼠标,甚至触控设备。

第八章:使用 JavaFX 进行交互式 Leap Motion 应用程序

现在我们来到了本书最激动人心的部分,我们将通过身体语言转化为命令来控制周围的物体和计算机,进入新的无触摸时代的计算机人交互。

每天我们都注意到输入界面的崛起,它们不再以鼠标为中心,而更倾向于无触摸输入。手势是人类如今可以自然地与机器交流的一种方式。

几十年来,动作控制一直在我们对未来的设想中占据着坚定的位置。我们看到了流行媒体中的超级英雄、疯狂科学家和太空牛仔只需挥动手就能控制数字体验。

使用 JavaFX 进行交互式 Leap Motion 应用程序

汤姆·克鲁斯通过手势进行计算

我们被这些强大、自然和直观的交互所吸引——想象一下如果我们能在自己的指尖上拥有这种力量会是什么样子。例如,《星际迷航》的全息甲板《未来报告》中的预犯预测计算机。你还记得汤姆·克鲁斯在后者中是如何通过透明显示屏上的手势进行计算的吗?所有这些都散发着一种力量和掌控感,同时又具有矛盾的简单、轻松、直观和人性化的感觉。简单地说,这些体验都是神奇的。

市场上有几种设备实际上允许我们仅使用身体的一些部分与计算机进行交互:许多Xbox游戏,微软游戏机,使用Kinect控制器来识别用户的身体动作。肌电臂带可以检测你肌肉的运动并将其转化为手势,以便你可以与计算机交互。Leap Motion 控制器可以识别用户的手和手指,并将动作和手势转化为计算机上的操作。

在本章中,您将学习使用Leap Motion设备进行手势识别,这是一种令人敬畏的设备,可以以无触摸的方式开发增强的 JavaFX 应用程序。

以下是本章将讨论的一些主题:

  • 介绍 Leap 控制器,它的工作原理以及如何获取

  • 获取和安装 SDK,配置其驱动程序,并验证其是否正常工作

  • 基于 Leap 的应用程序构建基础知识

  • 开发令人惊叹的无触摸 JavaFX 应用程序

Leap Motion 控制器

这是一个非常小的设备,高度为 13 毫米,宽度为 30 毫米,深度为 76 毫米,重量为 45 克(最终尺寸:0.5 英寸 x 1.2 英寸 x 3 英寸)。只需将 Leap Motion 软件运行在您的计算机上,将控制器插入 Mac 或 PC 上的 USB 接口,您就可以开始使用了(无需外部电源)。

它可以捕捉你手和手指的个别动作,几乎实时(200-300 fps),并将手势转化为计算机上运行的应用程序的不同操作。这款 79.99 美元的设备于 2013 年推出,称为 Leap Motion 控制器。

Leap Motion 控制器

Leap Motion 与人手的大小比较

从开发者的角度来看,这个设备允许设计应用程序,可以通过用户的手指的手势和动作来控制,就像未来报告中一样!

它能感知你自然的手部动作,并让你以全新的方式使用计算机——指向、挥动、伸手、抓取或拿起东西并移动。你可以做一些你从未梦想过的事情。

检查一下你的手;一只手有 29 根骨头,29 个关节,123 条韧带,48 条神经和 30 条动脉。这是复杂而复杂的。控制器已经非常接近完全弄清楚这一切。

实际上,当你考虑它时,Leap Motion 的魔力在于软件,但公司也在努力开发硬件来提供他们的技术。自 2011 年开始开发以来,它已经有了很大的进步,如下图所示:

Leap Motion 控制器

Leap Motion 控制器的演变

它是如何工作的

Leap Motion 的技术依赖于特殊的接收器硬件和定制软件,可以跟踪到 1/100 毫米的运动,没有可见的延迟时间。Leap Motion 控制器具有150 度的视野,并以 290fps 跟踪单独的手部和所有 10 个手指。

该设备的主要硬件由三个红外 LED 和两个单色红外(IR)摄像头组成。LED 产生红外光的 3D 点阵,摄像头以近乎 290fps 的速度扫描反射数据。在 50 厘米半径内的所有内容都将被扫描和处理,分辨率为 0.01 毫米。设备的主要组件如下图所示:

它是如何工作的

Leap Motion 控制器硬件层和内部组件

这是计算机交互的未来,Leap Motion 的非常快速和准确的自然用户界面以非常精确的方式将所有运动数据发送到计算机。数据将通过 Leap Motion 专有软件检测算法在主机计算机上进行分析,并且任何启用 Leap 的应用程序都可以直接进行接口连接,而无需使用任何其他物理输入设备。

坐标系统

在应用程序中使用 Leap Motion 控制器时,将从控制器接收的坐标值映射到适当的 JavaFX 坐标系统是一项基本任务。

从前面的讨论中,您可以观察到该设备可以在超宽的 150 度视野和深度的 z 轴内检测手部、手指和反射工具。这意味着您可以像在现实世界中一样在 3D 中移动您的手。

设备坐标系统使用右手笛卡尔坐标系,原点位于设备中心。如下图所示:

坐标系统

以设备为中心的坐标系统

每次设备扫描和分析您的手部运动数据时,都会生成一个包含所有已处理和跟踪数据的列表的 Frame 对象(手部、手指和工具),包括在帧中找到的一组运动手势(滑动、点击或圈)。

正如您可能已经注意到的,y 轴的正方向与大多数计算机图形系统(包括 JavaFX)中的向下方向相反。

然而,数据是指设备位置而不是屏幕,这与我们习惯于鼠标和触摸事件的方式发生了根本性的变化。

幸运的是,API 提供了几种有用的方法,可以随时找到我们的手和手指指向的位置。

获取设备

由于我们受到了这项令人惊叹的技术的启发,我们需要参与并开始使用该设备开发一些东西。因此,我们首先需要获得一个。

该设备可以从亚马逊、百思买等许多供应商处购买。但是,您也可以从 Leap Motion 商店(store-world.leapmotion.com)购买。

我在 2014 年底购买了我的设备,现在可能可以在一些商店找到特别折扣。

包内容

当您购买 Leap Motion 套装时,至少应包含以下图像中显示的物品:

包内容

Leap Motion 包内容

在撰写本文时,该套装包括:

  • Leap Motion 控制器

  • 两根定制长度的 USB 2.0 电缆

  • 欢迎卡

  • 重要信息指南

使用 Leap SDK 入门

现在我们已经有了硬件,我们需要安装软件并开始开发。这是一项非常简单的任务;只需将鼠标指向您喜欢的浏览器的地址栏,输入 URL developer.leapmotion.com/downloads,然后点击Enter键。

在撰写本文时,最新版本是 SDK 2.2.6.29154。单击您的操作系统图标以开始下载支持的版本。或者,只需单击带有标签Download SDK 2.2.6.29154 for OSX(适用于 Mac OS X)的绿色按钮。这将检测您的 PC /笔记本电脑操作系统,并允许您下载适合您操作系统的 SDK。

安装控制器驱动程序和软件

安装过程和准备好与设备交互需要一些简单的步骤。下载zip内容后,提取它,安装软件安装程序,一切都应该就位:

  1. 下载,提取并运行软件安装程序。

  2. 安装后,连接您的 Leap Motion 控制器并打开可视化器,如下面的屏幕截图所示:安装控制器驱动程序和软件

运行可视化器

  1. SDK 包括LeapJava.jar库和一堆用于控制器集成的本机库。在您的系统上集成LeapJava.jar的一种简单方法是将 JAR 添加到 Linux 或 Windows 的<JAVA_HOME>/jre/lib/ext(或 Mac 上的/Library/Java/Extensions)。

  2. 将本机库(Windows 的LeapJava.dllLeap.dllLeapd.dll;Mac 的libLeapJava.dyliblibLeap.dylib;Linux 的libLeapJava.solibLeap.so)复制到<JAVA_HOME>/jre/bin文件夹中。

  3. 或者,您可以将 JAR 作为依赖项添加到每个项目中,并将本机库作为 VM 参数-Djava.library.path=<native library path>加载。

注意

SDK 还包括许多基于支持语言的示例,包括HelloWorld.java示例,这是一个非常好的起点,可以帮助您了解如何将控制器与 Java 应用程序集成。

验证是否有效

如果一切正常,一个小的 Leap Motion 图标应该出现在任务栏通知区域(Windows)或菜单栏(Mac)上,并且应该是绿色的,就像前面的屏幕截图所示。设备上的 LED 指示灯应该亮起绿色,并且面向您以正确定位设备

如果您能够与可视化器交互并看到手指和手的可视化,就像下面的屏幕截图所示,那么现在是开始开发的时候了。

验证是否有效

Leap Motion 诊断可视化器应用程序

支持的语言

在深入研究我们的应用程序之前,我想简单提一下,Leap Motion SDK 支持许多语言,包括 Java 和其他语言,如 JavaScript 用于 Web,C#,C ++,Python,Unity,Objective-C 和虚幻游戏引擎。

Leap JavaFX 应用程序

像您一样,我迫不及待地想要开始开发过程,现在您将学习如何与连接到 Leap motion 设备的 JavaFX 8 基于 3D 的应用程序进行无触摸交互。

鉴于本书迄今为止尚未涵盖 3D API,这是一个很好的机会,简要描述 3D API 并将 Leap Motion v2 骨骼建模(3D 手)与一些 3D 交互带入我们的 JavaFX 应用程序。

Leap Motion API v2.0 引入了一个新的骨骼跟踪模型,提供有关手和手指的额外信息,预测不清晰可见的手指和手的位置,并改进整体跟踪数据。有关 API 的更多信息,请访问developer.leapmotion.com/documentation/java/devguide/Intro_Skeleton_API.html?proglang=java

我们将展示如何将 Leap Motion v2 的新骨骼模型轻松集成到 JavaFX 3D 场景中。我们将使用 JavaFX 提供的预定义 3D 形状 API,快速创建 3D 对象。这些形状包括盒子、圆柱体和球体,我们将在我们的应用程序中使用它们。

一览 JavaFX 3D API

3D 意味着三维或者拥有宽度高度深度(或长度)的东西。我们的物理环境是三维的,我们每天都在三维空间中移动。

JavaFX 3D 图形库包括 Shape3D API,JavaFX 中有两种类型的 3D 形状:

  • 预定义的形状:这些形状是提供的,以便让你更快地创建 3D 对象。这些形状包括盒子、圆柱体和球体。

  • 用户定义的形状:JavaFX Mesh 类层次结构包含TriangleMesh子类。三角网格是 3D 布局中最常见的网格类型。

在我们的应用程序中,我们将使用预定义的形状。有关 JavaFX 3D API 和示例的更多信息,请访问docs.oracle.com/javase/8/javafx/graphics-tutorial/javafx-3d-graphics.htm

更多学习资源

在 SDK 中捆绑的HelloWorld.java示例是一个丰富的资源,它将帮助你在 Leap Motion 控制器和普通 Java 应用程序之间的开发和集成过程中。

另一个讨论与 Java 集成的资源是 Leap Motion 文档中的Getting Started with Java Development部分,网址为developer.leapmotion.com/documentation/java/devguide/Leap_Guides.html

基本应用程序结构

在查看了HelloWorld.java示例和文档示例之后,你会注意到以下几点:

  • 我们需要一个Controller对象,允许 Leap 设备和应用程序之间的连接。

  • 我们需要一个Listener子类来处理来自控制器的事件。

  • 手势跟踪在onConnect()方法中启用。

  • 这个类中的主要方法是onFrame(),这是一个callback方法,当一个新的带有运动跟踪数据的Frame对象可用时被调用。这个对象包含手、手指或工具的列表,以及它们的位置、方向和运动速度的几个向量。

  • 如果启用了手势,我们还将得到一个基于最后几帧分析的手势列表。此外,你将知道手势的状态,无论它是刚开始、正在进行中还是已经结束。

JavaFX 8 3D 应用程序

我们将讨论的应用程序是一个复杂的 JavaFX 8 3D 应用程序,它将帮助你了解基于 Leap 的应用程序开发结构,与设备交互以识别手部位置,并与手势交互以在 3D 环境中建模我们的手。

你可以在后面的示例部分找到更多资源,包括开发基于 Leap 的 JavaFX 应用程序的更高级概念。

在这个应用程序中,我们将检测骨骼、手臂和关节(位置和方向),以圆柱体和球体的形式在我们的 JavaFX 应用程序SubScene中建模我们的手。然后,我们将检测它们的位置,以在 Leap Motion 设备上建模我们真实的手部运动。

我们还将添加原始的image,这样你就可以在应用程序的背景中看到模型和你的真实手。

该应用程序由三个类组成:

  • LeapListener.java:这个类是监听器,它与 Leap Motion 控制器线程交互,将所有分析的数据(手臂、骨骼、手指和关节)传输到 JavaFX 应用程序中。

  • LeapJavaFX.java:这个类是一个 JavaFX 应用程序线程,它将与LeapListener.java交互,以便在每一帧中创建 3D 形状,而不需要跟踪以前的形状。由于 Observable JavaFX bean 属性的强大功能,它允许从 Leap 线程传输的数据被渲染到 JavaFX 线程中。

  • Pair.java:这是一个小的便利类,用于存储每个关节中链接的两根骨头。

所以,让我们开始看看我们如何做到这一点。

提示

您必须通过在“跃动”控制面板中勾选“允许图像”选项并确保在“跟踪”选项卡下禁用“鲁棒模式”选项来启用 Leap Motion 控制面板上的图像。

工作原理

首先,我们将解释我们的应用程序的主要桥梁,即 Leap 事件监听器LeapListener.java

开发 JavaFX 应用程序时的主要问题是如何将 JavaFX 线程与其他非 JavaFX 线程混合,而在我们的情况下是 Leap Motion 事件Listener子类,它以非常高的速率处理事件。

为了将这些事件传递到 JavaFX 线程,我们将在LeapListener.java类中使用BooleanProperty对象。由于我们只会监听doneList对象的更改,因此我们不需要列表也是可观察的,因为它们将在任何更改时触发事件(添加一个骨骼)。

因此它们是普通列表,我们只使用一个布尔可观察属性,在创建每个 LeapFrame对象中的所有列表后将其设置为 true:

private final BooleanProperty doneList= new
SimpleBooleanProperty(false);
private final List<Bone> bones=new ArrayList<>();
private final List<Arm> arms=new ArrayList<>();
private final List<Pair> joints=new ArrayList<>();
private final List<WritableImage> raw =new ArrayList<>();

要获取原始图像,我们必须在onInit()中设置此策略,并且出于隐私原因,用户还必须在 Leap Motion 控制面板中启用该功能,以便任何应用程序都可以获取原始摄像头图像。

@Override
public void onInit(Controller controller){
 controller.setPolicy(Controller.PolicyFlag.POLICY_IMAGES);
}

如您所知,如果要处理手势,这是您启用此功能的地方,因此也许您可以将它们保持注释状态。

让我们继续创建 Frame 方法:

@Override
public void onFrame(Controller controller) {
  Frame frame = controller.frame();
  doneList.set(false);
  doneList.set(!bones.isEmpty() || !arms.isEmpty());
}
public BooleanProperty doneListProperty() {
  return doneList;
}

对于每一帧,重置doneList,处理数据,最后如果我们有骨骼或手臂,则将其设置为true(如果没有手放在 Leap 上,帧仍在处理中)。将属性公开以便在 JavaFX 应用程序中进行监听。

现在处理帧对象数据。首先是图像(这可以在最后完成)。在每一帧上清除列表,然后检索图像(从左右摄像头)。如果您想了解其工作原理,Leap 文档非常有帮助。访问developer.leapmotion.com/documentation/java/devguide/Leap_Images.html

实际上,这段代码是第一个示例的一部分,添加了PixelWriter以生成 JavaFX 图像。由于 Leap 提供了明亮的像素,我对它们进行了否定处理(1- (r|g|b))以获得负图像,在手部更加清晰可见。此外,我将图像从左到右翻转,如下所示:

(newPixels[i*width+(width-j-1)]).raw.clear();
ImageList images = frame.images();
for(Image image : images){
  int width = (int)image.width();
  int height = (int)image.height();
  int[] newPixels = new int[width * height];
  WritablePixelFormat<IntBuffer> pixelFormat = PixelFormat.getIntArgbPreInstance();
  WritableImage wi=new WritableImage(width, height);
  PixelWriter pw = wi.getPixelWriter();
  //Get byte array containing the image data from Image object
  byte[] imageData = image.data();

  //Copy image data into display object
  for(int i = 0; i < height; i++){
  for(int j = 0; j < width; j++){
    //convert to unsigned and shift into place
    int r = (imageData[i*width+j] & 0xFF) << 16;
    int g = (imageData[i*width+j] & 0xFF) << 8;
    int b = imageData[i*width+j] & 0xFF;
    // reverse image
    newPixels[i*width+(width-j-1)] = 1- (r | g | b);
  }
  }
  pw.setPixels(0, 0, width, height, pixelFormat, newPixels, 0,width);
  raw.add(wi);
}

然后清除骨骼、手臂和关节列表,如下所示的代码:

bones.clear();
arms.clear();
joints.clear();
if (!frame.hands().isEmpty()) {
Screen screen = controller.locatedScreens().get(0);
if (screen != null && screen.isValid()){

获取骨骼列表;对于找到的每个手指,迭代该手指的骨骼类型(最多 5 个),以避免戒指和中指的掌骨。代码如下:

for(Finger finger : frame.fingers()){
  if(finger.isValid()){
  for(Bone.Type b : Bone.Type.values()) {
    if((!finger.type().equals(Finger.Type.TYPE_RING) &&!finger.type().equals(Finger.Type.TYPE_MIDDLE)) ||!b.equals(Bone.Type.TYPE_METACARPAL)){
          bones.add(finger.bone(b));
      }
    }
  }
}

现在我们将遍历手列表以获取每只手臂,并将其添加到手臂列表中,如下所示:

for(Hand h: frame.hands()){
  if(h.isValid()){
  // arm
  arms.add(h.arm());

现在获取手指关节。详细解释如何获取每个关节有点复杂。基本上,我找到每只手的手指,识别除拇指以外的其他四只手指。代码如下:

FingerList fingers = h.fingers();
Finger index=null, middle=null, ring=null, pinky=null;
for(Finger f: fingers){
  if(f.isFinger() && f.isValid()){
    switch(f.type()){
    case TYPE_INDEX: index=f; break;
    case TYPE_MIDDLE: middle=f; break;
    case TYPE_RING: ring=f; break;
    case TYPE_PINKY: pinky=f; break;
    }
  }
}

一旦我识别出手指,我就定义了它们之间的每对关节(前三个关节)和手腕的关节(最后一个)。代码如下:

// joints
if(index!=null && middle!=null){
  Pair p=new Pair(index.bone(Bone.Type.TYPE_METACARPAL).nextJoint(),middle.bone(Bone.Type.TYPE_METACARPAL).nextJoint());
  joints.add(p);
  }
  if(middle!=null && ring!=null){
    Pair p=new Pair(middle.bone(Bone.Type.TYPE_METACARPAL).nextJoint(),
    ring.bone(Bone.Type.TYPE_METACARPAL).nextJoint());
    joints.add(p);
  }
  if(ring!=null && pinky!=null){
    Pair p=new Pair(ring.bone(Bone.Type.TYPE_METACARPAL).nextJoint(),
    pinky.bone(Bone.Type.TYPE_METACARPAL).nextJoint());
    joints.add(p);
  }
  if(index!=null && pinky!=null){
    Pair p=new Pair(index.bone(Bone.Type.TYPE_METACARPAL).prevJoint(),pinky.bone(Bone.Type.TYPE_METACARPAL).prevJoint());
    joints.add(p);
  }

最后,上述代码返回骨骼集合的新副本,以避免在迭代此列表时出现并发异常。请注意,Leap 的帧速率非常高。在一台性能强大的计算机上,它几乎是 5-10 毫秒。代码如下:

public List<Bone> getBones(){
 return bones.stream().collect(Collectors.toList());
}

这比 JavaFX 脉冲更快(60 fps,或大约 16 毫秒),因此在渲染骨骼时可以更改列表。通过这种clone方法,我们避免了任何并发问题。

LeapJavaFX 应用程序的 Listener 方法如下:

Override
  public void start(Stage primaryStage) {
    listener = new LeapListener();
    controller = new Controller();
    controller.addListener(listener);

初始化 Leap 监听器类和控制器,然后添加监听器:

final PerspectiveCamera camera = new PerspectiveCamera();
camera.setFieldOfView(60);
camera.getTransforms().addAll(new Translate(-320,-480,-100));
final PointLight pointLight = new PointLight(Color.ANTIQUEWHITE);
pointLight.setTranslateZ(-500);
root.getChildren().addAll(pointLight);

为 3DsubScene创建一个透视摄像机,将其平移到屏幕中间底部,并对用户进行翻译。还要添加一些点光源。代码如下:

rawView=new ImageView();
rawView.setScaleY(2);

为 Leap 图像创建一个ImageView,尺寸为 640 x 240,鲁棒模式关闭(在 Leap 控制面板中取消选中该选项),因此我们在 Y 轴上进行缩放以获得更清晰的图像。代码如下:

Group root3D=new Group();
root3D.getChildren().addAll(camera, root);
SubScene subScene = new SubScene(root3D, 640, 480, true,
SceneAntialiasing.BALANCED);
subScene.setCamera(camera);
StackPane pane=new StackPane(rawView,subScene);
Scene scene = new Scene(pane, 640, 480);

创建一个带有相机的组,并将光源作为subScene的根。请注意,启用了深度缓冲和抗锯齿以获得更好的渲染效果。相机也添加到了subScene

主根将是一个StackPane:背面是ImageView,前面是透明的SubScene。代码如下:

final PhongMaterial materialFinger = new PhongMaterial(Color.BURLYWOOD);
final PhongMaterial materialArm = new PhongMaterial(Color.CORNSILK);

为手指和手臂设置材料,使用漫射颜色:

listener.doneListProperty().addListener((ov,b,b1)->{
  if(b1){
    ...
  }
});

我们监听doneList的变化。每当它为true(每帧之后!),我们处理 3D 手部渲染:

List<Bone> bones=listener.getBones();
List<Arm> arms=listener.getArms();
List<Pair> joints=listener.getJoints();
List<WritableImage> images=listener.getRawImages();

首先,获取骨骼、手臂和关节集合的最新副本。然后,如果在 JavaFX 线程中有有效图像,我们将图像设置在ImageView上,并删除除光源之外的所有根节点(因此我们重新创建手部骨骼):

Platform.runLater(()->{
    if(images.size()>0){
    // left camera
    rawView.setImage(images.get(0));
  }
  if(root.getChildren().size()>1){
    // clean old bones
    root.getChildren().remove(1,root.getChildren().size()-1);
}

骨骼 迭代列表并将骨骼添加到场景中。如果集合发生变化,我们在其副本上进行迭代时不会出现并发异常。

bones.stream().filter(bone -> bone.isValid() && bone.length()>0).forEach(bone -> {

现在我们为每根骨骼创建一个圆柱体。这涉及一些计算。如果你想深入了解,可以将每根骨骼视为一个带有位置和方向的向量。创建一个垂直圆柱体,其半径为骨骼宽度的一半,高度与长度相同。然后,分配材料。代码如下:

final Vector p=bone.center();
// create bone as a vertical cylinder and locate it at its center position
Cylinder c=new Cylinder(bone.width()/2,bone.length());
c.setMaterial(materialFinger);

然后,我们用真实骨骼方向与垂直方向进行叉乘;这给出了旋转的垂直向量。(符号是由于坐标系的变化)。ang对象是这两个向量之间的角度。可以应用一个转换,将其旋转到ang围绕给定向量的中心。代码如下:

// translate and rotate the cylinder towards its direction
final Vector v=bone.direction();
Vector cross = (new Vector(v.getX(),-v.getY(), v.getZ())).cross(new Vector(0,-1,0));
double ang=(new Vector(v.getX(),-v.getY(),-v.getZ())).angleTo(new Vector(0,-1,0));
c.getTransforms().addAll(new Translate(p.getX(),-p.getY(),-p.getZ()),new Rotate(-Math.toDegrees(ang), 0, 0, 0, new Point3D(cross.getX(),-cross.getY(),cross.getZ())));
  // add bone to scene
root.getChildren().add(c);

现在在每根骨骼的开头和结尾都有球体:

// add sphere at the end of the bone
Sphere s=new Sphere(bone.width()/2f);
s.setMaterial(materialFinger);
s.getTransforms().addAll(new Translate(p.getX(),-p.getY()+bone.length()/2d,-p.getZ()),new Rotate(-Math.toDegrees(ang), 0, -bone.length()/2d, 0, new Point3D(cross.getX(),-cross.getY(),cross.getZ())));
  // add sphere to scene
  root.getChildren().add(s);
  // add sphere at the beginning of the bone
  Sphere s2=new Sphere(bone.width()/2f);
  s2.setMaterial(materialFinger);
  s2.getTransforms().addAll(new Translate(p.getX(),-p.getY()-bone.length()/2d,-p.getZ()),new Rotate(Math.toDegrees(ang), 0, bone.length()/2d, 0, new Point3D(cross.getX(),-cross.getY(),cross.getZ())));
  // add sphere to scene
  root.getChildren().add(s2);
});

现在对于关节;我们再次使用圆柱体。连接的两个元素之间的距离给出长度,我们获取位置和方向来生成和转换圆柱体。代码如下:

joints.stream().forEach(joint->{
  double length=joint.getV0().distanceTo(joint.getV1());
  Cylinder c=new Cylinder(bones.get(0).width()/3,length);
  c.setMaterial(materialArm);
  final Vector p=joint.getCenter();
  final Vector v=joint.getDirection();
  Vector cross = (new Vector(v.getX(),-v.getY(), v.getZ())).cross(new Vector(0,-1,0));
  double ang = (new Vector(v.getX(),-v.getY(),-v.getZ())).angleTo(new Vector(0,-1,0));
  c.getTransforms().addAll(new Translate(p.getX(),-p.getY(),-p.getZ()), new Rotate(-Math.toDegrees(ang), 0, 0, 0, new Point3D(cross.getX(),-cross.getY(),cross.getZ())));
  // add joint to scene
  root.getChildren().add(c);
});

最后,我们从肘部到手腕的距离中获取长度。所有这些都在 API 中:developer.leapmotion.com/documentation/java/api/Leap.Arm.html。代码如下:

arms.stream().
filter(arm->arm.isValid()).
forEach(arm->{
  final Vector p=arm.center();
  // create arm as a cylinder and locate it at its center position
  Cylinder c=new Cylinder(arm.width()/2,arm.elbowPosition().
  minus(arm.wristPosition()).magnitude());
  c.setMaterial(materialArm);
  // rotate the cylinder towards its direction
  final Vector v=arm.direction();
  Vector cross = (new Vector(v.getX(),-v.getY(),-v.getZ())).cross(new Vector(0,-1,0));
  double ang=(new Vector(v.getX(),-v.getY(),-v.getZ())).
  angleTo(new Vector(0,-1,0));
  c.getTransforms().addAll(new Translate(p.getX(),-p.getY(),-p.getZ()),new Rotate(- Math.toDegrees(ang), 0, 0, 0, new Point3D(cross.getX(),- cross.getY(),cross.getZ())));
  // add arm to scene
  root.getChildren().add(c);
});

运行应用程序

恭喜!现在连接您的 Leap 控制器(leap 图标应该是绿色的)并运行您的应用程序。如果一切正常,您应该最初看到一个空的应用程序场景,如下截图所示:

运行应用程序

Leap JavaFX 应用程序的初始运行

移动并挥动你的手,你的手的骨骼建模应该出现在你真实的手背景中,响应你的真实动作如下所示:

运行应用程序

Leap JavaFX 应用程序与 Leap 控制器的交互

尝试不同的手臂或手部模式和位置;您应该在 JavaFX 应用程序场景中看到这一复制,如下截图所示:

运行应用程序

Leap JavaFX 应用程序与 Leap 控制器的交互,具有不同的手部模式

更多示例

有关使用 JavaFX 与 Leap Motion 设备的更多示例,请参考在线资源,如www.parleys.com/share.html#play/525467d6e4b0a43ac12124adjperedadnr.blogspot.com.es/2013/06/leap-motion-controller-and-javafx-new.html。有关与其他编程语言的交互,请访问developer.leapmotion.com/gallery

总结

在本章中,你了解了令人印象深刻的 Leap Motion 设备,以及使用它来增强 JavaFX 应用程序所产生的非常好的组合效果。

你开始学习关于设备及其工作原理。接下来,我们讨论了它的 Java SDK,并探讨了一个简单的应用程序,在这个应用程序中,你学会了如何在一个线程中监听和处理 Leap 设备的数据,同时在 JavaFX 线程中触发事件来处理场景图中的数据。

在下一章中,我将提供给真正的 JavaFX 专家们使用的高级工具和资源。

附录 A:成为 JavaFX 大师

您的 JavaFX 8 之旅会在这里停止吗?绝对不会!JavaFX 是一个非常庞大的主题,它每天都在增长,从 Oracle 的核心发布到新功能、功能和稳定性,再到许多社区个人和公司创建的第三方库,以填补您可能遇到的任何缺失,或者围绕它发明新的缺失。

当然,通过这本书,我无法涵盖所有 JavaFX 8 主题。相反,我试图挖掘许多 JavaFX 领域的表面,并打开主题关键,以便使您的冒险更加轻松,通过找到自己的方式,并了解如何自己做。

然而,我们还讨论了许多其他工具和技术,通过开发传统的 Web 和桌面应用程序,然后转向更先进和市场需求的领域,即移动开发。

我们通过学习物联网来探索未来,这是信息技术的下一个时代,我们涵盖了更多有趣的主题。我们在电影中多次看到了运动,并且想象过,通过开发基于令人惊叹的 Leap Motion v2 设备的增强型无触摸 JavaFX 8,我们实现了我们的梦想。

还有更多内容可以成为 JavaFX 专题,并获得其他经验,这些内容我们在本书中没有讨论。

那么,我们接下来该去哪里?

现在,既然您已经有了许多运行中的 JavaFX 8 应用程序,并且对它在许多平台和硬件上的工作原理有了了解,剩下的就取决于您和您的创造力了。

加入 Facebook、Twitter 社区,并关注技术专家的博客,JavaFX 博客网址为blogs.oracle.com/javafx/,并在fxexperience.com/找到新闻、演示和见解。最重要的是,进行实验。

在本章结束时,一定要查看许多在今天的生产中使用 JavaFX 的框架项目

资源和参考资料

在这一部分,您将找到许多有用的链接和参考资料,将帮助您进一步了解所有 JavaFX 主题。

官方文档

  • JavaFX 文档:这是一个很好的资源,指向所有 JavaFX 资源、新闻、经验、视频、书籍、API 文档、技术文章和教程:

www.oracle.com/technetwork/java/javase/documentation/javafx-docs-2159875.html

JavaFX 示例

学习 JavaFX 8 的最佳资源之一是 Java 开发工具包 8 的示例和演示,其中包括一个 JavaFX 演示文件夹,其中包含许多精彩和先进的应用程序,涵盖了所有 JavaFX 主题,并附有源代码,您可以自己进行实验。

您可以通过访问以下链接www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html下载示例,然后转到Java SE Development Kit 8u45 Demos and Samples Downloads表,选中接受许可协议单选按钮,然后单击与您的操作系统相关的 zip 文件链接,如下图所示:

JavaFX samples

下载 JDK 和 JavaFX 8 演示和示例

samples zip 文件包含预构建的示例,您可以运行,以及每个示例的 NetBeans 项目文件。

解压缩 zip 文件会产生以下目录结构:

--src  (*Contains a NetBeans project for each sample*)
 --<Sample1>
  --nbproject
  --src
  --build.xml
  --manifest.mf
  --<Sample2>
  <sample1>.jar(*Runs the sample as a standalone application*)
<sample2>.jar

JavaFX samples

JavaFX 示例文件夹内容

任何sample.jar都可以作为独立应用程序运行;双击 JAR 文件,我们有四个应用程序:

  1. Ensemble8.jar:演示各种 JavaFX 功能的示例应用程序库,包括动画,图表和控件。对于每个示例,您可以在所有平台上执行以下操作:
  • 查看并与运行中的示例进行交互

  • 阅读其描述。

您只能在桌面平台上为每个示例执行以下操作:

  • 复制其源代码

  • 对于几个示例,您可以调整示例组件的属性

  • 如果您连接到互联网,还可以跟随链接到相关的 API 文档Ensemble8 也可以在 JavaFX 上运行 ARM(意味着在 Raspberry Pi 上运行)

JavaFX 示例

Ensemble8 应用程序正在运行

  1. MandelbrotSet.jar:演示使用 Java Parallel API 进行并行执行的优势的示例应用程序。

该应用程序使用 Mandelbrot 集算法渲染图像,并提供直观的输入参数范围内的导航。

更多信息可在MandelbrotSet文件夹内的index.html文件中找到。

JavaFX 示例

MandelbrotSet 应用程序正在运行

  1. Modena.jar:演示使用Modena主题的 UI 组件外观和感觉的示例应用程序。它为您提供了对比ModenaCaspian主题以及探索这些主题的各个方面的选项。JavaFX 示例

Modena 应用程序正在运行

  1. 3DViewer.jar:3DViewer 是一个示例应用程序,允许用户使用鼠标或触摸板导航和检查 3D 场景。3DViewer 具有OBJ和 Maya 文件中一些功能的导入器。

还提供了导入 Maya 文件动画的功能。(请注意,在 Maya 文件的情况下,保存为 Maya 文件时应删除所有对象的构造历史。)3DViewer 还具有将场景内容导出为 Java 或FXML文件的功能。

JavaFX 示例

3DViewer 应用程序正在运行

为了自己玩代码并尝试任何更改,恭喜,您有机会通过在NetBeans中运行所有先前提到的应用程序来做到这一点:

  1. 在 NetBeans IDE 中,单击工具栏中的打开项目,或单击文件菜单,然后选择打开项目

  2. 导航到解压缩示例的位置,并在src目录中,选择一个项目,然后单击打开

  3. 要在 NetBeans IDE 中运行应用程序,请在项目窗格中,右键单击项目,然后选择运行

Java SE 8

作为提醒,JavaFX 8 内置于 Java 8 SDK 中。这意味着您只需要下载 Java 8 SDK。可以从以下位置下载 Java 8 软件开发工具包和相关信息:

  • Oracle 技术网络上的 Java 8:

oracle.com/java8

  • Java 开发工具包:

www.oracle.com/technetwork/java/javase/downloads/index.html

  • Java 8 中有什么新功能?让我们来看看 Java 8 的新功能:

www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html

  • Java SE 8 新功能巡回演示:

tamanmohamed.blogspot.com/2014/06/java-se-8-new-features-tour-big-change.html

Java SE 8 API 文档和教程

Java 8 文档和指南位于以下链接:

  • Java SE 8 Javadoc API 文档:

docs.oracle.com/javase/8

  • JavaFX 8 Javadoc API 文档:

docs.oracle.com/javase/8/javafx/api

  • Java SE 8 概述文档:

docs.oracle.com/javase/8/docs/index.html

  • Java SE 8 教程:

docs.oracle.com/javase/tutorial/tutorialLearningPaths.html

项目 Lambda

核心 Java SE 8 添加的语言特性是 Lambda 表达式和流 API。以下参考资料是围绕项目 Lambda 的路线图、博客和视频:

  • Lambda 的现状,Brian Goetz(Oracle):

cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

  • Java 8 揭秘:Lambda、默认方法和批量数据操作,Anton Arhipov:

zeroturnaround.com/rebellabs/java-8-revealed-lambdas-defaultmethods-and-bulk-data-operations

  • Java 8 中 Lambda 表达式和流的 10 个示例,Javin Paul:

javarevisited.blogspot.com/2014/02/10-example-of-lambdaexpressions-in-java8.html

  • Java SE 8:Lambda 快速入门,Oracle:

www.oracle.com/webfolder/technetwork/tutorials/obe/java/Lambda-QuickStart/index.html

  • Java 8:闭包,Lambda 表达式解密,Frank Hinkel:

frankhinkel.blogspot.com/2012/11/java-8-closures-lambdaexpressions.html

Nashorn

Java SE 8 包括一个名为Nashorn的新脚本引擎,这是 Java 运行时的一个新的、改进的 JavaScript 引擎。该引擎使开发人员能够使用 JavaScript 语言来编写应用程序。

以下链接和参考资料是描述 Nashorn 的文章和博客:

  • Oracle 的 Nashorn:JVM 的下一代 JavaScript 引擎,Julien Ponge:

www.oraclejavamagazine-digital.com/javamagazine_twitter/20140102/?pg=60#pg60

  • Open JDK 的 Nashorn 网站:

wiki.openjdk.java.net/display/Nashorn/Main

  • Nashorn 博客:

blogs.oracle.com/Nashorn

JavaFX 属性和绑定

在 JavaFX 中,属性和绑定在同步 JavaFX 节点之间的值时至关重要。

以下是关于只读属性、监听器和 JavaFX Bean 角色的重要资源:

  • 在 JavaFX 中创建只读属性,Michael Heinrichs:

blog.netopyr.com/2012/02/02/creating-read-only-properties-injavafx

  • 未知的 JavaBean,Richard Bair:

weblogs.java.net/blog/rbair/archive/2006/05/the_unknown_jav.html

  • 使用 JavaFX 属性和绑定,Scott Hommel:

docs.oracle.com/javafx/2/binding/jfxpub-binding.htm

  • Pro JavaFX 8, (Chapter 4, Properties and Bindings), Johan Vos, James Weaver, Weiqi Gao, Stephen Chin, and Dean Iverson, (Apress, 2014):

www.apress.com/9781430265740

  • Open Dolphin:一个 JavaFX MVC 框架(由 Canoo Engineering 的 Dierk Koenig 创建):

open-dolphin.org/dolphin_website/Home.html

  • 基于约定优于配置和依赖注入的 JavaFX MVP 框架(由 Adam Bien 创建):

afterburner.adam-bien.com

JavaFX 社区

所以你想参与 JavaFX 社区?请查看以下链接:

  • Java.net JavaFX 社区网站:

www.java.net/community/javafx

  • FXExperience:JavaFX 新闻、演示和见解(@fxexperience):

fxexperience.com

  • Nighthacking(@_nighthacking):由 Stephen Chin 主持。环游世界,了解有关 Java、JavaFX 和物联网的一切。令人惊叹的现场讲话。

nighthacking.com

  • Oracle 的 JavaFX 社区门户网站,提供真实世界的用例、社区支持、第三方工具和 Open JFX:

www.oracle.com/technetwork/java/javase/community/index.html

  • JFXtras:JavaFX 自定义控件社区:

jfxtras.org

  • ControlsFX:另一个由 Oracle 的 Jonathan Giles 发起的自定义控件社区:

fxexperience.com/controlsfx

  • 硅谷 JavaFX 用户组:

www.meetup.com/svjugfx

  • 硅谷 JavaFX 用户组直播:

www.ustream.tv/channel/silicon-valley-javafx-user-group

  • Oracle 关于 JavaFX 的论坛:

community.oracle.com/community/developer/english/java/javafx/javafx_2.0_and_later

Java SE / JavaFX 图书和杂志

以下链接是与新的 Java SE 8 和 JavaFX 8 平台相关的较新的书名:

  • 一本了不起的书,《JavaFX 8:通过示例介绍,第二版》,Carl Dea,Mark Heckler,Gerrit Grunwald,José Pereda 和 Sean M. Phillips(Apress,2014 年。ISBN:978-1-4302-6460-6)

www.apress.com/9781430264606

  • Pro JavaFX 8,Johan Vos,James Weaver,Weiqi Gao,Stephen Chin 和 Dean Iverson(Apress,2014 年。ISBN:978-1-4302-6574-0)

www.apress.com/9781430265740

  • Java 8 Recipes,Josh Juneau(Apress,2014 年。ISBN:978-1-4302-6827-7)

www.apress.com/9781430268277

  • JavaFX Rich Client Programming on the NetBeans Platform,Paul Anderson 和 Gail Anderson(Addison-Wesley Professional,2014 年。ISBN:978-0321927712):

blogs.oracle.com/geertjan/entry/new_book_javafx_rich_client

www.amazon.com/JavaFX-Client-Programming-NetBeans-Platform/dp/0321927710

  • 《掌握 JavaFX 8 控件》,Hendrik Ebbers(Oracle Press,2014 年。ISBN:9780071833776):

mhprofessional.com/product.php?isbn=0071833773

www.guigarage.com/javafx-book

  • JavaFX 快速入门指南,J.F. DiMarzio(Oracle Press,2014 年。ISBN:978-0071808965):

www.mhprofessional.com/product.php?isbn=0071808965

  • Java SE 8 for the Really Impatient,Cay S. Horstmann(Addison-Wesley,2014 年。ISBN 978-0321927767)

www.addison-wesley.de/9780321927767.html

  • 《掌握 Lambda》,Maurice Naftalin(Oracle Press,2014 年。ISBN:007-1829628):

www.mhprofessional.com/product.php?isbn=0071829628

  • 来自 Oracle 的 Java 杂志:

www.oracle.com/technetwork/java/javamagazine/index.html

感谢您的时间,希望您和我写作时一样喜欢阅读这本书。谢谢。

posted @ 2025-09-10 15:11  绝不原创的飞龙  阅读(92)  评论(0)    收藏  举报