JavaFX 2.0 企业应用程序最佳实践(第 1 部分)

作者:James L. Weaver

本文是由两部分组成的系列文章的第一部分,重点介绍如何在 JavaFX 2.0 中使用最佳实践开发企业应用程序。

2012 年 4 月发布

下载:

下载Java FX

下载NetBeans IDE

下载示例项目 (ZIP)

简介

JavaFX 2.0 是一个用于创建富互联网应用程序 (RIA) 的 API 和运行时。JavaFX 于 2007 年推出,2011 年 10 月发布了 2.0 版本。JavaFX 2.0 的一个优点是可以使用成熟、熟悉的工具用 Java 语言编写代码。

本文是由两部分组成的系列文章的第一部分,重点介绍如何在 JavaFX 2.0 中使用最佳实践开发企业应用程序。

TweetBrowser 应用程序概述

为说明 JavaFX 2.0 企业应用程序开发中的一些最佳实践,我们将探讨一个名为 TweetBrowser 的示例应用程序。如图 1 所示,该应用程序包含以下内容:

  • 一个 Toolbar,包含一个 TextField 和两个 Button 控件,用于搜索和浏览从 Twitter REST API 获取的微博。
  • 一个 ListView,其单元包含微博的表示。每条微博由 ListCell 的一个子类表示,包含一个 ImageView 以展示个人资料图片,以及一些 Hyperlink 控件以便用户能够浏览屏幕名称、hashtag 和 Web 链接。
  • 一个在执行搜索时旋转的 ProgressIndicator,另外还有一个 WebView,用于显示微博中 Web 链接的关联 Web 页面。

您将在下一节中下载 TweetBrowser 项目,其中包含该应用程序的代码,本文将着重探讨其部分代码。

图 1:TweetBrowser 应用程序启动时的屏幕截图

图 1:TweetBrowser 应用程序启动时的屏幕截图

单击 #hashtag@screenname 时,将在 UI 右上角显示一个旋转进度指示器(如图 2 所示),指示正在进行搜索。还可以在文本域键入一个 #hashtag@screenname 或文字,然后按 Enter 键或单击 Search 按钮。无论用哪种方式,Search 按钮的显示都将变成 X,指示您可以通过单击该按钮取消搜索并禁用大部分 UI。

图 2:TweetBrowser 搜索时的屏幕截图

图 2:TweetBrowser 搜索时的屏幕截图

单击某条微博中的 Web 链接将打开一个弹出式窗口,其中包含显示所选页面的 WebView,如图 3 所示。

图 3:单击某个超链接后的 WebView

图 3:单击某个超链接后的 WebView

获取并运行 TweetBrowser 项目

  • 下载 NetBeans 项目文件,它包括 TweetBrowser 项目。
  • 将该项目解压缩到您选择的目录。
  • 启动 NetBeans,选择 File -> Open Project
  • 在 Open Project 对话框中,转到所选目录后打开 TweetBrowser 项目,如图 4 所示。如果收到一个声明无法找到 jfxrt.jar 文件的消息,单击 Resolve 按钮并转到 JavaFX 2.0 SDK 安装目录下面的 rt/lib 文件夹。

:可以从 NetBeans 站点获取 NetBeans IDE。

图 4:在 NetBeans 中打开 TweetBrowser 项目

图 4:在 NetBeans 中打开 TweetBrowser 项目

  • 要运行该应用程序,请单击工具栏上的 Run Project 图标或按 F6 键。Run Project 图标外观类似媒体(如 DVD)播放器上的 Play 按钮,如图 5 所示。

图 5:在 NetBeans 中运行 TweetBrowser 程序

图 5:在 NetBeans 中运行 TweetBrowser 程序

TweetBrowser 应用程序应显示在一个窗口中,如前面图 1 中所示。继续试用该应用程序,浏览屏幕名称、hashtag 和 Web 链接。下面我们将分析该应用程序并探讨其中一些代码。

分析 TweetBrowser 应用程序

在深入研究代码之前,我们先来分析图 6 所示的各个片段,这些片段共同组成了 TweetBrowser 应用程序。
图 6:TweetBrowser 应用程序示意图

图 6:TweetBrowser 应用程序示意图

从图 6 右上角的 TweetBrowser 主页开始,用户可以通过在其中单击大眼鸟图标启动应用程序。如果尚未运行 TweetBrowser 的实例,这会通过 Java Web Start 调用应用程序。有关 TweetBrowser 主页的 URL,请参考“另请参见”一节。

图 6 左下部代表 tweetbrowser.ui 软件包,它包含 2 个 Java 类和 1 个 JavaFX 级联样式表 (CSS):

  • TweetBrowserMain 是该 JavaFX 应用程序的主类,它扩展了 Application,包含 main()start() 方法。其在 TweetBrowser 应用程序中的作用是创建一个场景并在其中填充图 1 所示的 ToolBarListView。此外,TweetBrowserMain 还创建和有条件地显示一个弹出窗口,其中包含前面图 3 中所见的 WebView
  • TweetCell 是一个 ListCell 扩展类,它呈现一条微博表示,如图 1 所示。每条微博有一个 TweetCellTweetCell 实例包含在 ListView 中。
  • tweetbrowser.css 是一个 JavaFX CSS,它规定了应用程序中各种节点的样式,这些节点包括 Back 和 Search 按钮以及编写微博的用户的屏幕名称。

图 6 中下部代表 tweetbrowser.model 软件包,它包含 3 个 Java 类:

  • TweetBrowserModel 是应用程序的主模型类,它包含表示应用程序状态的属性。它还包含使用 REST/FX 库的方法(如图 6 右下角所示)查询 Twitter API。REST/FX 是 JavaFX 2.0 外部的一个库,这里使用它来与 Twitter REST 端点通信并分析其 JSON 响应。本文末尾的“另请参见”一节提供到 REST/FX 项目的链接。
  • Tweet 是一个表示微博的类,它遵循针对 JavaFX 属性规定的规范。例如,Tweet 包含一个名为 idStringProperty,该属性可通过 setId()getId()idProperty() 方法访问。
  • HistoryStack 实现了一个 FIFO 栈,用于管理 hashtag、文字和屏幕名称的导航历史。Back 按钮使用此历史记录确定在单击它时显示哪个 hashtag、文字或屏幕名称。

如图 6 所示,tweetbrowser.ui 软件包中的类调用 tweetbrowser.model 软件包中的类的方法。此外,tweetbrowser.ui 软件包中的类将模型的状态呈现到 UI,这主要是通过利用 JavaFX 的绑定功能实现的。

TweetBrowser 应用程序中使用的技巧和最佳实践

下面将讨论 TweetBrowser 应用程序中使用的一些技巧和最佳实践:

  • 从应用程序主页通过 Java Web Start 调用应用程序
  • 确保只启动应用程序的一个实例
  • 将 UI 绑定到模型

首先从列表中的第一项开始:从应用程序主页通过 Java Web Start 调用应用程序。

从应用程序主页通过 Java Web Start 调用应用程序

基于浏览器的应用程序有一个吸引人的特性,就是这些应用程序可以通过给定的 Web 页面即时访问。用户对此已有期待,因此为了让访问富客户端 Java 应用程序的用户感觉更加舒适,可以在 Web 页面上提供一个 Java Web Start 链接。通过这种方式,用户可能感觉很像是在运行一个 Web 应用程序,因为单击一个图标即会打开一个包含应用程序的窗口。在用户看来,新窗口包含了应用程序,而他们并不关心窗口是什么类型(浏览器窗口或 Java 应用程序窗口)。

清单 1 显示了 TweetBrowser 应用程序主页的 HTML 和 JavaScript 源代码,是对 NetBeans 中构建的 TweetBrowser 项目的输出进行修改的结果。

<html><head>

<script src="http://java.com/js/dtjava.js"></script>

<script>

    function launchApplication(jnlpfile) {

        dtjava.launch(            {

                url : 'TweetBrowser.jnlp'},

            {

                javafx : '2.0+'

            },

            {}

        );

        return false;

    }

</script>

</head>

<body style="font-family: Arial; font-size: 12pt;">

  <h2 >TweetBrowser Home Page</h2>

  <a href='#'><img title="Launch TweetBrowser app"

     src="img/tweetbrowser-logo-100.png"

     onclick="return launchApplication('TweetBrowser.jnlp');"/></a>

  <p>Click the icon to launch TweetBrowser via Java Web Start</p>

</body></html>

清单 1:应用程序主页的示例 HTML 和 JavaScript 代码

清单 2 包含用于启动 TweetBrowser 应用程序的 Java 网络启动协议 (JNLP) 的示例源代码,也是对 NetBeans 中构建的 TweetBrowser 项目的输出进行修改的结果。

<?xml version="1.0" encoding="utf-8"?>

<jnlp spec="1.0" xmlns:jfx="http://javafx.com" href="TweetBrowser.jnlp">

  <information>

    <title>TweetBrowser</title>

    <vendor>Jim Weaver</vendor>

    <description>Sample JavaFX 2.0 application.</description>

    <offline-allowed/>

  </information>

  <resources os="Windows" arch="x86">

    <jfx:javafx-runtime version="2.0+"

    href="http://download.oracle.com/otn-pub/java/javafx/...code omitted..."/>

  </resources>

  <resources os="Windows" arch="x64">

    <jfx:javafx-runtime version="2.0+"

    href="http://download.oracle.com/otn-pub/java/javafx/...code omitted..."/>

  </resources>

  <resources>

    <j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se"/>

    <jar href="TweetBrowser.jar" size="49305" download="eager" />

    <jar href="lib/restfx-1.0.jar" size="49092" download="eager" />

    <jar href="lib/restfx-server-1.0.jar" size="6884" download="eager" />

  </resources>

  <jfx:javafx-desc  width="800" height="600"

    main-class="javafxpert.tweetbrowser.ui.TweetBrowserMain" 

    name="TweetBrowser" />

  <update check="background"/>

</jnlp>

清单 2:启动应用程序的 JNLP 的示例代码

NetBeans 生成的源代码与清单 1 和 2 所示的源代码之间的主要差别就在于删除了与小程序相关的代码,这让 TweetBrowser 只能通过 Java Web Start 来访问。

有关如何通过 Java Web Start 启动 JavaFX 应用程序的详细说明,请参考“另请参见”一节提到的部署 JavaFX 应用程序 指南。

确保只启动应用程序的一个实例

使用 Java Web Start 方法时要考虑的一个问题是确保只调用应用程序的一个实例,即,当已有一个实例在运行时,无论用户是否单击应用程序图标,都只运行一个实例。为此,TweetBrowser 应用程序利用了 javafx.jnlp 软件包中的一些类,如清单 3 中显示的 TweetBrowserMain.javastart() 方法中所示:

  @Override public void start(final Stage primaryStage) {

    stage = primaryStage;

    try {

      singleService =

          (SingleInstanceService)ServiceManager

          .lookup("javax.jnlp.SingleInstanceService");

      singleListener = new SingleInstanceListener() {

        @Override public void newActivation(String[] params) {

          System.out.println("TweetBrowser instance already running");

          primaryStage.toFront();

        }

      };

      if (singleService != null) {

          singleService.addSingleInstanceListener(singleListener);

      }

    }

    catch (UnavailableServiceException use) {

      singleService = null;

      System.out.println("Single instance service not loaded");

    }

    primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {

      @Override public void handle(WindowEvent e) {

        if (singleService != null) {

          singleService.removeSingleInstanceListener(singleListener);

        }

      }

    });

    Scene scene = SceneBuilder.create()

      .width(1000)

      .height(660)

      .stylesheets("javafxpert/tweetbrowser/ui/tweetbrowser.css")

      .root(

        BorderPaneBuilder.create()

          .top(createToolBar())

          .center(createListView())

          .build()

      )

      .build();

    listView.disableProperty().bind(TweetBrowserModel.instance

        .queryActive.or(TweetBrowserModel.instance.webViewPopupVisible));

    invokeSearch(FIRST_SEARCH_TERM, true);

    createWebViewPopup();

    progressIndicator.visibleProperty()

                     .bind(TweetBrowserModel.instance.queryActive

                     .or(TweetBrowserModel.instance.webViewPopupWebEngine

                     .getLoadWorker().runningProperty()));   

    stage.setScene(scene);

    stage.setTitle("Tweet Browser");

    stage.show();

}

清单 3:TweetBrowserMain.java start() 方法

通过 Java Web Start 启动运行一个实例后,如果用户试图启动应用程序,则会调用 SingleInstanceListener 的覆盖 newActivation() 方法,而不是启动另一个 TweetBrowser 实例。为了在应用程序关闭时进行清理,为主窗口的 setOnCloseRequest() 方法提供了一个事件处理器,它将删除 SingleInstanceListener

:为满足编译器的要求,项目在编译时使用了 jnlp.jar 文件,但在运行时可以使用 Java 部署工具包提供的 javafx.jnlp 类(如清单 3 所示)。

将 UI 绑定到模型

多年来,使用模型/视图/控制器(即 MVC)模式已成为软件开发中的一个常见做法。通过提供将 UI 属性绑定到模型中的属性的功能,JavaFX API 有助于采用 MVC 模式。

如清单 4 所示,TweetBrowser 应用程序实例化并引用名为 TweetBrowserModel 的主模型类。

public class TweetBrowserModel {

public static TweetBrowserModel instance = new TweetBrowserModel();

public BooleanProperty queryActive = new SimpleBooleanProperty(false);

public BooleanProperty webViewPopupVisible =

                                     new SimpleBooleanProperty(false);

public ObservableList allTweets = FXCollections.observableArrayList();

...

}

清单 4:实例化并引用模型

TweetBrowserMainTweetCell 类中使用该引用将 UI 绑定到模型。例如,清单 3 中 progressIndicatorvisibleProperty 绑定到一个表达式,该表达式中包含清单 4 中模型的 queryActive 属性。

清单 5 是将 UI 绑定到模型的另一个示例,其中将 listViewitems 属性绑定到清单 4 中的 allTweets ObservableList

  private Node createListView() {

    listView = ListViewBuilder.create()

      .items(TweetBrowserModel.instance.allTweets)

      .editable(false)

      .build();

    listView.setCellFactory(new Callback<ListView<Tweet>, ListCell<Tweet>>() {

      @Override

      public ListCell<Tweet> call(ListView<Tweet> list) {

        ListCell tweetCell = new TweetCell();

        tweetCell.setEditable(false);

        return tweetCell;

      }

    });

    return listView;

}

清单 5:TweetBrowserMain.java createListView() 方法

注意,绑定行为内置在 ListView 控件的 items 属性中,因此在本例中不使用 bind 方法。

总结

从应用程序主页通过 Java Web Start 调用应用程序,确保只启动应用程序的一个实例,将 UI 绑定到模型,这些技巧的使用让用户和开发人员的生活更加轻松。

在本系列文章的第 2 部分,我们将探讨 TweetBrowser 示例应用程序中使用的更多技巧和最佳实践。

另请参见

关于作者

James L. (Jim) Weaver 是一位 Java 和 JavaFX 开发人员、作者和演讲者,积极致力于促进富客户端 Java 和 JavaFX 成为新应用程序开发的首选技术。Jim 撰写的著作包括《Inside Java》、《Beginning J2EE》和《Pro JavaFX 2》。他的专业背景包括 15 年的 EDS 系统架构师,以及同样年数的独立开发人员。作为一名 Oracle Java 宣讲师,Jim 在许多国际软件技术会议上发表过演讲,包括在旧金山和圣保罗的 JavaOne 大会。Jim 的博客为 http://javafxpert.com,Tweet 为 @javafxpert,电子邮件联系方式为 james.weaver AT oracle.com