开发流程和HelloWorld

这里我们简单编写一个HelloWorld,实现一个登录功能,并演示JavaFX的开发流程。

例子工程

我们这里有一个登陆窗口,启动之后要求用户输入用户名和密码才能继续操作。如果用户输入了正确的用户名和密码,就会跳转到主页,如果输入错误,就会弹出警告框提示用户。这里我们只是简单的将用户名和密码硬编码到Java代码中作为校验的条件。

主页没什么实际的功能,左侧的树形菜单会在窗口初始化时载入数据,演示initialize()方法的使用。

创建工程

这部分就不介绍了,在第一节JavaFX简介和环境搭建中已经介绍过了,我们这里按照之前额的叙述,创建一个Maven工程即可。

「画」界面

使用JavaFX相比Swing最大的好处就是可以使用SceneBuilder和FXML布局,MVC层次更加清晰。我们这里再src/main/resources下创建两个FXML文件作为「登录页面」和「主页」的布局文件。

至于SceneBuilder如何使用,这个就真的没什么好说的了,用过Swing、Qt、GTK、WinForm等任何一款图形界面框架都有这种GUI设计器,拖拖拽拽设置点属性就可以。如果不知道某个控件属性是什么意思,我们可以参考该控件的JavaDoc,FXML中的属性和控件的API基本是对应的。

初始化和窗口控制

我们这里由于有两个窗口,在JavaFX中窗口对应的概念叫做Stage,Stage中加载Scene(场景),Scene包含一个可修改的组件树。

所以这里我们需要准备两个Stage,并为其分别加载FXML布局。Stage本身就是单例的,这里我们将两个Stage对象设置为了App的静态成员,便于外部访问。

App.java

package com.gacfox.demo.jfxdemo01;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class App extends Application {

    // 该应用的所有窗口
    public static Stage loginWindow = null;
    public static Stage mainWindow = null;

    @Override
    public void start(Stage stage) throws IOException {

        // 初始化Scene并加载FXML
        Parent loginRoot = FXMLLoader.load(getClass().getResource("/fxml/Login.fxml"));
        Scene loginScene = new Scene(loginRoot);
        Parent mainRoot = FXMLLoader.load(getClass().getResource("/fxml/Main.fxml"));
        Scene mainScene = new Scene(mainRoot);

        // 初始化窗口
        App.loginWindow = stage;
        App.loginWindow.setScene(loginScene);
        App.mainWindow = new Stage();
        App.mainWindow.setScene(mainScene);
        App.mainWindow.initOwner(loginWindow);

        // 显示登录窗口
        App.loginWindow.show();
    }

    public static void appMain(String[] args) {
        launch();
    }
}

注意:start()方法会给出一个应用默认的Stage,我们新实例化的Stage要用initOwner()设置默认窗口的父窗口。

控制器和FXML

我们的界面除了FXML布局,还要有业务逻辑,这些代码写在Controller类中。

在FXML中,通过在根元素上使用fx:controller指定控制器类。

login.fxml

<BorderPane fx:id="bpLoginPane" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.gacfox.demo.jfxdemo01.controller.LoginController">
...
</BorderPane>

控件如果需要在控制器中访问,需要设置fx:id属性:

login.fxml

<TextField fx:id="tfUserName" GridPane.columnIndex="1" />

然后在控制器中,使用@FXML标注,控件的Java实例就会自动注入了。

@FXML
private TextField tfUserName;

要注意的是,fx:id值必须和对象的字段名对应。

一些交互组件可以用onAction指定绑定的控制器方法,比如按钮可以直接绑定默认点击事件。

<Button fx:id="btnLogin" mnemonicParsing="false" onAction="#login" text="登录" GridPane.columnIndex="1" GridPane.rowIndex="2" />

当然,更通用的方法是在初始化时,通过代码绑定事件。

LoginController

下面代码是登录窗口的逻辑。

LoginController.java

package com.gacfox.demo.jfxdemo01.controller;

import com.gacfox.demo.jfxdemo01.App;

import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.BorderPane;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;

public class LoginController {
    @FXML
    private BorderPane bpLoginPane;
    @FXML
    private TextField tfUserName;
    @FXML
    private PasswordField tfPassword;
    @FXML
    private Button btnLogin;

    public void login() {
        String username = tfUserName.getText();
        String password = tfPassword.getText();
        if ("root".equals(username) && "123456".equals(password)) {
            // 登陆成功
            App.loginWindow.hide();
            App.mainWindow.show();
        } else {
            // 登录失败
            Alert alert = new Alert(AlertType.WARNING);
            alert.setTitle("警告");
            alert.setHeaderText("用户名或密码错误");
            alert.setContentText("该行为会被报告给管理员!");
            alert.initOwner(this.bpLoginPane.getScene().getWindow());
            alert.show();
        }
    }
}

上面代码非常简单,用户名和密码校验通过,就进行窗口切换。如果校验不通过,就使用Alert弹出一个警告窗口。

MainController

MainController.java

package com.gacfox.demo.jfxdemo01.controller;

import javafx.fxml.FXML;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;

public class MainController {

    @FXML
    private TreeView<String> tvBssMenu;

    @FXML
    public void initialize() {
        // 模拟一些TreeView的数据并加载
        TreeItem<String> root = new TreeItem<>("功能菜单");
        root.setExpanded(true);

        TreeItem<String> ti1 = new TreeItem<>("资金管理");
        TreeItem<String> ti2 = new TreeItem<>("库存管理");
        TreeItem<String> ti3 = new TreeItem<>("人力管理");

        root.getChildren().add(ti1);
        root.getChildren().add(ti2);
        root.getChildren().add(ti3);

        tvBssMenu.setRoot(root);
    }
}

上面代码主要演示initialize()的使用,该方法是是一个Scene的生命周期方法,使用@FXML标注,会在该Scene初始化时回调,我们在初始化时为TreeView加载了一些数据。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。