JavaFX 8 :Chapter 1 开始(2)

来源:《Learn JavaFX 8: Building User Experience and Interfaces with Java 8》

向JavaFX应用程序传递参数

与Java应用程序一样,您可以将参数传递给JavaFX应用程序。以下有两种方式传递参数给JavaFX应用程序:

  • 在独立应用程序的命令行中
  • 在applet和WebStart应用程序的Java网络启动协议(JNLP)文件中。

Parameters类是Application类的静态内部类,它用来封装传递给JavaFX应用程序的参数。它将参数分为以下三种:

  • 命名参数
  • 未命名参数
  • 原始参数(命名参数和未命名参数的组合)

需要使用Parameters类的以下三种方法来访问这三种类型的参数:

  • Map getNamed()
  • List getUnnamed()
  • List getRaw()

参数可以是命名的也可以是未命名的。一个命名参数由一对(名称,值)组成。一个未命名的参数由单个值组成。getNamed()方法返回一个Map<String, String>,其中包含名称参数的键值对。getUnnamed()方法返回List<String>,其中每个元素都是一个未命名的参数值。
您只向JavaFX应用程序传递命名参数和未命名参数,不传递原始类型的参数。JavaFX运行时通过Parameters类的getRaw()方法使传递给应用程序的所有参数(命名的和未命名的)作为List<String>被返回。下面的讨论将清楚地说明这三个方法返回值之间的区别。
Application类的getParameters()方法返回Application.Parameters类的引用。对Parameters类的引用可以在Application类的init()方法和随后执行的代码中获得。参数在应用程序的构造函数中不可用,因为构造函数是在init()方法之前调用的。在构造函数中调用getParameters()方法将返回null。
表1-7中的程序读取传递给应用程序的所有类型的参数,并将它们显示在TextArea中。TextArea是一个显示多行文本的UI节点。

表1-7. 访问传递给JavaFX应用程序的参数

// FXParamApp.java
package com.jdojo.intro;
import java.util.List;
import java.util.Map;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

public class FXParamApp extends Application {
    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage stage) {
        // Get application parameters
        Parameters p = this.getParameters();
        Map<String, String> namedParams = p.getNamed();
        List<String> unnamedParams = p.getUnnamed();
        List<String> rawParams = p.getRaw();
 
        String paramStr = "Named Parameters: " + namedParams + "\n" +
        "Unnamed Parameters: " + unnamedParams + "\n" +
        "Raw Parameters: " + rawParams;
        TextArea ta = new TextArea(paramStr);
        Group root = new Group(ta);
        stage.setScene(new Scene(root));
        stage.setTitle("Application Parameters");
        stage.show();
    }
}

让我们来看一些向FXParamApp类传递参数的情况。当您运行FXParamApp类时,在以下情况中,上述提及的输出被显示在有TextArea控件的窗口中。

案例一

如果这个类是作为一个独立的应用程序而运行的,请使用以下命令:

java com.jdojo.stage.FXParamApp Anna Lola

上面的命令传递了零个命名参数和两个未命名参数:Anna和Lola。原始参数列表将包含两个未命名参数。输出结果如下所示:

Named Parameters: {}
Unnamed Parameters: [Anna, Lola]
Raw Parameters: [Anna, Lola]

案例二

如果这个类是作为一个独立的应用程序而运行的,请使用以下命令:

java com.jdojo.stage.FXParamApp Anna Lola width=200 height=100

上面的命令不传递命名参数,尽管最后两个参数似乎是作为命名参数传递的。在命令行上对参数使用等号(=)不会使该参数成为命名参数。下一个例子解释了如何从命令行传递命名参数。

它传递了四个未命名的参数:Anna、Lola、width=200和height=100。原始参数列表将包含四个未命名参数。输出结果如下所示:

Named Parameters: {}
Unnamed Parameters: [Anna, Lola, width=200, height=100]
Raw Parameters: [Anna, Lola, width=200, height=100]

案例三

要从命令行传递一个命名参数,您需要在参数前面加上两个连字符(--)。也就是说,一个命名参数应该以如下形式输入:

--key=value

使用如下命令,这个类将作为一个独立的应用程序而运行:

java com.jdojo.stage.FXParamApp Anna Lola --width=200 --height=100

上面的命令传递两个命名参数:width=200和height=100。它传递了两个未命名参数:Anna和Lola。原始参数列表将包含四个元素:两个命名参数和两个未命名参数。原始参数列表中的命名参数值以两个连字符开头。输出结果如下所示:

Named Parameters: {height=100, width=200}
Unnamed Parameters: [Anna, Lola]
Raw Parameters: [Anna, Lola, --width=200, --height=100]

案例四

FXParamApp类是作为一个applet或WebStart应用程序而运行的。在这些情况下,有不同的方法来指定命名参数和未命名参数。但是,应用程序的内部会以相同的方式访问它们。注意,当使用getRaw()方法访问一个命名参数时,它前面有两个连字符。但是,在web和WebStart部署文件中指定命名参数时,不能在命名参数前加两个连字符。

使用WebStart启动FXParamApp应用程序的JNLP文件的部分内容如下所示。它指定了两个命名参数和两个未命名参数:

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0" xmlns:jfx="http://javafx.com" href="FX_NetBeans_Only.jnlp">
...
    <jfx:javafx-desc ... >
        <fx:param name="width" value="200"/>
        <fx:param name="height" value="100"/>
        <fx:argument>Anna</fx:argument>
        <fx:argument>Lola</fx:argument>
    </jfx:javafx-desc>
</jnlp>

启动JavaFX应用程序

在前面,我谈到了在开发JavaFX第一个应用程序时启动JavaFX应用程序的主题。本节将提供更多关于启动JavaFX应用程序的细节。

每个JavaFX应用程序类都继承自Application类。Application类在javafx.application包中。它包含一个静态的launch()方法。它的唯一目的是启动一个JavaFX应用程序。它是一个重载的方法,有以下两种变体:

  • static void launch(Class<? extends Application> appClass, String... args)
  • static void launch(String... args)

注意,您没有创建JavaFX应用程序类的对象去启动它。当调用launch()方法时,JavaFX在运行时会创建应用程序类的一个对象。

JavaFX应用程序类必须有一个无参数的构造函数,否则当试图启动它时将引发运行时异常。

launch()方法的第一个变体很清晰。您将应用程序类的类引用作为第一个参数传递,launch()方法将创建该类的一个对象。第二个参数由传递给应用程序的命令行参数组成。下面的代码片段展示了如何使用launch()方法的第一个变体:

public class MyJavaFXApp extends Application {
    public static void main(String[] args) {
        Application.launch(MyJavaFXApp.class, args);
    }
    // More code goes here
}

传递给launch()方法的类引用不必与调用该方法的类相同。例如,下面的代码片段从MyAppLauncher类启动了MyJavaFXApp应用程序类,它没有扩展Application类:

public class MyAppLauncher {
    public static void main(String[] args) {
        Application.launch(MyJavaFXApp.class, args);
    }
    // More code goes here
}

launch()方法的第二个变体只接受一个参数,即传递给应用程序的命令行参数。它使用哪个JavaFX应用程序类去启动应用程序?它尝试根据调用者查找应用程序类名。它检查调用它的代码的类名。如果调用了该方法的代码所在的类直接或间接的继承了Application类,则将使用该类启动JavaFX应用程序。否则,将抛出运行时异常。让我们看一些例子来明确这条规则。

在下面的代码片段中,launch()方法检测到它是从MyJavaFXApp类的main()方法调用的。MyJavaFXApp类继承自Application类。因此,MyJavaFXApp类被用作应用程序类:

public class MyJavaFXApp extends Application {
    public static void main(String[] args) {
        Application.launch(args);
    }
    // More code goes here
}

在下面的代码片段中,Test类的main()方法调用了launch()方法。因为Test类没有继承Application类,所以,抛出一个运行时异常,如下面的代码输出所示:

public class Test {
    public static void main(String[] args) {
        Application.launch(args);
    }
    // More code goes here
}
Exception in thread "main" java.lang.RuntimeException: Error: class Test is not a subclass 
of javafx.application.Application
    at javafx.application.Application.launch(Application.java:211)
    at Test.main(Test.java)

在下面的代码片段中,launch()方法检测到它是被MyJavaFXApp$1类的run()方法调用的。注意,MyJavaFXApp$1类是编译器生成的一个匿名内部类,它是Object类的子类,而不是Application类的子类,并且它实现了Runnable接口。因为对launch()方法的调用是在MyJavaFXApp$1类中,而这个类不是Application类的子类,所以抛出了一个运行时异常,如下面的代码输出所示:

public class MyJavaFXApp extends Application {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            public void run() {
                Application.launch(args);
            }
        });
        t.start();
    }
    // More code goes here
}
Exception in thread "Thread-0" java.lang.RuntimeException: Error: class MyJavaFXApp$1 is 
not a subclass of javafx.application.Application
    at javafx.application.Application.launch(Application.java:211)
    at MyJavaFXApp$1.run(MyJavaFXApp.java)
    at java.lang.Thread.run(Thread.java:722)

现在你知道了如何启动JavaFX应用程序,是时候去学习启动JavaFX应用程序的最佳实践了:将main()方法中的代码限制为一句话来启动应用程序,如下所示:

public class MyJavaFXApp extends Application {
    public static void main(String[] args) {
        Application.launch(args);
        // Do not add any more code in this method
    }
    // More code goes here
}

Application类的launch()方法只能被调用一次,否则将引发运行时异常。对launch()方法的调用会阻塞,直到应用程序终止。并不总是需要main()方法来启动JavaFX应用程序。JavaFX打包程序会为您合成一个。例如,当您使用NetBeans IDE时,您不需要有main()方法,如果有,NetBeans会忽略它。

JavaFX应用程序的生命周期

JavaFX运行时创建了几个线程。在应用程序的不同阶段,线程用于执行不同的任务。在本节中,我将只解释那些在Application类的生命周期中被用来调用Application类的方法的线程。JavaFX运行时在其他线程中创建了两个线程:

  • JavaFX-Launcher
  • JavaFX Application Thread

Application类的launch()方法创建了这些线程。在JavaFX应用程序的生命周期内,JavaFX运行时按顺序调用指定的JavaFX应用程序类的以下方法:

  • The no-args constructor
  • The init() method
  • The start() method
  • The stop() method

JavaFX运行时在JavaFX应用程序线程上创建一个指定的应用程序类的对象。JavaFX Launcher线程调用指定的Application类的init()方法。Application类中的init()方法是空的,您可以在应用程序类中重写此方法。在JavaFX Launcher线程上创建Stage或Scene是不被允许的。它们必须在JavaFX应用程序线程中创建。因此,您不能在init()方法中创建Stage或Scene。尝试这样做会引发运行时异常。但init()方法可以创建UI控件,例如按钮或形状。

JavaFX应用程序线程调用指定的应用程序类的start(Stage Stage)方法。注意,Application类中的start()方法被声明为抽象的,您必须在应用程序类中重写此方法。

此时,launch()方法等待JavaFX应用程序完成。当应用程序完成时,JavaFX应用程序线程调用指定的应用程序类的stop()方法。在Application类中,stop()方法的默认实现是空的。您必须在应用程序类中重写此方法,以便在应用程序停止时执行您的逻辑。

表1-8中的代码说明了JavaFX应用程序的生命周期。它显示的是一个空舞台。在显示舞台时,您将看到输出的前三行。您需要关闭舞台才能看到输出的最后一行。

表1-8. JavaFX应用程序的生命周期

// FXLifeCycleApp.java
package com.jdojo.intro;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class FXLifeCycleApp extends Application {
    public FXLifeCycleApp() {
        String name = Thread.currentThread().getName();
        System.out.println("FXLifeCycleApp() constructor: " + name);
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void init() {
        String name = Thread.currentThread().getName();
        System.out.println("init() method: " + name);
    }

    @Override
    public void start(Stage stage) {
        String name = Thread.currentThread().getName();
        System.out.println("start() method: " + name);
        Scene scene = new Scene(new Group(), 200, 200);
        stage.setScene(scene);
        stage.setTitle("JavaFX Application Life Cycle");
        stage.show();
    }

    @Override
    public void stop() {
        String name = Thread.currentThread().getName();
        System.out.println("stop() method: " + name);
    }
}
FXLifeCycleApp() constructor: JavaFX Application Thread
init() method: JavaFX-Launcher
start() method: JavaFX Application Thread
stop() method: JavaFX Application Thread

终止JavaFX应用程序

JavaFX应用程序可以显式终止或者隐式终止。您可以通过调用Platform.exit()方法显式终止JavaFX应用程序。当调用此方法时,在start()方法之后或从start()方法内部调用Application类的stop()方法,然后终止JavaFX应用程序线程。此时,如果只有守护线程在运行,那么JVM将退出。如果从Application类的构造函数或init()方法调用Platform.exit()方法,则可能不会调用stop()方法。

JavaFX应用程序可以在web浏览器中运行。在web环境中调用Platform.exit()方法可能没有任何效果。

当最后一个窗口关闭时,JavaFX应用程序可能隐式终止。可以使用Platform类的静态方法setImplicitExit(boolean implicitExit)来打开或关闭此行为。将true传入此方法会隐式终止JavaFX应用程序,将false传递给此方法则不会终止。默认情况下,该行为是打开的。这就是为什么到目前为止,在大多数示例中,当您关闭窗口时,应用程序会被终止。当这个行为被打开时,Application类的stop()方法会在终止JavaFX Application Thread之前被调用。终止JavaFX应用程序线程并不总是终止JVM。如果所有运行的非守护线程都终止,则JVM终止。如果关闭了JavaFX应用程序的隐式终止行为,则必须调用Platform类的exit()方法来终止应用程序。

总结

JavaFX是一个基于java的开源的GUI框架,用于开发富客户端应用程序。它是在Java平台上GUI开发技术领域中Swing的继承者。

JavaFX中的GUI被显示在一个舞台中。舞台是Stage类的一个实例。舞台是桌面应用程序中的窗口和web应用程序中的浏览器区域。一个舞台包含一个场景。一个场景包含一组以树状结构排列的节点(图形)。

JavaFX应用程序继承了Application类。JavaFX运行时创建了被称为初始舞台的第一个舞台,并调用了应用程序类中的start()方法,传递初始舞台的引用。开发人员需要向舞台添加一个场景,并使舞台在start()方法中可见。

您可以使用Application类的launch()方法启动JavaFX应用程序。如果您运行继承了应用程序类的Java类(即JavaFX应用程序类),则Java命令会自动为您启动JavaFX应用程序。

在JavaFX应用程序的生命周期内,JavaFX运行时以特定的顺序调用JavaFX application类的预定义方法。首先,调用该类的无参构造函数,然后调用init()方法和start()方法。当应用程序终止时,stop()方法被调用。

可以通过调用Platform.exit()方法来终止JavaFX应用程序。当应用程序作为applet在web浏览器中运行时,调用Platform.exit()方法可能不会产生任何效果。

下一章将介绍JavaFX中的属性和绑定。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值