J1002.JavaFX简介

时间:2023-03-08 18:36:53
J1002.JavaFX简介

引言

2008年12月05日,SUN发布了JavaFX第一个正式版本,以期望Java在UI端能够更好地应用于开发富客户端的互联网应用(Rich Internet Cliet)。

2011年发布的JavaFX2.0取消之前基于Script的模式,改为使用原生Java结合CSS等进行了重构。

2014年发布的JavaFX8.0,与JDK1.8进行整合,并运用JDK8的新特性。

我们在上一篇文章中,已经简要地介绍了JavaUI框架的发展历史及特性。从这篇文章开始介绍JavaFX的特性,使用方法,优缺点以及注意事项等。

总体来说,使用JavaFX开发界面,要比Swing、SWT等要快捷一些。但作为一个已经发展了10多年的“主流”Java UI框架,在功能丰富程度以及一些处理细节上,还有比较多的欠缺甚至不方便、官方无解的问题。举几个例子:

1、JavaFX的Alert对话话比Swing提供的好用、漂亮,但是在Swing中使用JavaFX Alert时,由于其各自的派遣线程的问题,无法使用Modal模式。这种问题简直让人抓狂。

2、TableView在样式、某些处理机制方面,同样不可理喻。默认情况下,不管TableView中有多少数据,都会为整个表格绘制表格线;在编辑单元格时,必须敲回车键才能提交修改,如果点击其它单元格、其它行,或者敲Tab键,TableView将自动Cancel修改的内容;提供的TableCell太少,功能太弱。像必须敲回车才能提交修改的问题,开发人员N久以前就作为Bug提交给SUN/Oracle了,有人说JDK9会修复这个问题,但实际上我看了JDK9.0.1中相关的源代码,并没有。开发人员写了几行较详细的注释,说明这个问题不是他的责任,你们自行想办法。

这类问题都留给开发人员自行解决,个人感觉是十分说不过去的。在开发lrJAP平台的过程中,我数次被这样的问题折腾,甚至在日志中写下了这样一段话:

2017-08-25,越来越感觉JavaFX与主流JS(如ExtJS)有差距,今天已经特别想放弃JavaFX,而且特别到一定要把这句话记下来。唉。

拔特,问题总是能够被解决的,所以我们继续。先介绍介绍一下JavaFX与JS类框架相比的一些特性。

1、JavaFX已经与JDK融合,理论上可以使用JDK所有功能,以及所有的第三方Java类库,只要资源能够加载到JVM中,JavaFX就可以使用这些资源。这为我们在各方面都提供了便利。并且降低了上手的难度。

2、提供了较丰富的基础UI组件以及布局。特别是BorderPane,有利于界面模式的开发。可以通过CSS进行代码风格控制。

3、提供了基础的数据绑定功能,可以实现View-Model之间的双向数据(属性)绑定,并能够实现较复杂的事件处理机制。简化了与数据展现、控制相关的代码。

4、提供了使用比较方便的事件机制,并且可以与JDK8提供的Lamda结合,简化事件处理的代码。

5、提供了一些基本的图表。如饼图、折线图等。

个人感觉,JavaFX比较适用于以下业务场景:

1、基于企业内部局域网的应用,或基于相对比较封闭的网络环境下的集团级应用。也就是应用于传统的企业内部管理、业务系统的开发。

2、适用于业务数据结构复杂、业务处理逻辑复杂、有一定的性能需求,但对界面炫酷程度没有极端要求的应用。

3、功能界面可以被抽象、归类,希望在此基础上,实现快速开发。

基于JavaFX的应用如果运行在Internet环境中,则安全、带宽等方面要有相应的保证。个人感觉,对于一般用户来说,使用基于JavaFX的应用,主要困难在于下载、安装、配置JRE,以及基于信任、安全方面的考虑。

第一个JavaFX程序:HelloWorld

所有示例程序,除非特别说明,否则一律基于以下主要环境:Win10,IntelliJ IDEA,JDK 1.8_151。

为了能够更清楚地说明代码结构以及处理细节,代码中的注释有点过度:

package com.lirong.javafx.demo;

import javafx.application.Application;

import javafx.geometry.Insets;

import javafx.scene.Scene;

import javafx.scene.control.Alert;

import javafx.scene.control.Button;

import javafx.scene.layout.BorderPane;

import javafx.scene.layout.HBox;

import javafx.stage.Stage;

public class HelloWorldUI extends Application {

private static final double DEFAULT_BUTTOH_HEIGHT = 30;

public static void main(String[] args) {

// 启动应用

Application.launch(args);

}

@Override

public void start(Stage stage) throws Exception {

Button buttonHelloWorld = new Button("HelloWorld");

// 设置按钮大小

buttonHelloWorld.setPrefSize(100, DEFAULT_BUTTOH_HEIGHT);

// 事件处理

buttonHelloWorld.setOnAction(action -> {

// 弹出模态对话框

Alert alert = new Alert(Alert.AlertType.INFORMATION);

alert.setContentText("Hello World!");

alert.initOwner(stage);

alert.showAndWait();

});

// 用于验证ToolBar中组件之间间隔的按钮,仅显示,无事件处理

Button buttonOther = new Button("Other");

buttonOther.setPrefSize(80, DEFAULT_BUTTOH_HEIGHT);

// 模拟工具栏效果

HBox toolBar = new HBox();

// 设置ToolBar中控件与边界上、下、左、右的距离

toolBar.setPadding(new Insets(10));

// 设置ToolBar中控件之间的间隔

toolBar.setSpacing(10);

// 为ToolBar增加元素

toolBar.getChildren().addAll(buttonHelloWorld, buttonOther);

// 设置最外层的容器

BorderPane container = new BorderPane();

container.setTop(toolBar);

// 处理应用场景,默认窗口大小为800*600

Scene scene = new Scene(container, 800, 600);

stage.setTitle("'Hello World' JavaFX Application");

stage.setScene(scene);

stage.show();

}

}

运行效果如下图:

J1002.JavaFX简介

与程序逻辑无关的代码量比较少,默认的界面风格还说得过去。

第一个JavaFX Embedded in Swing程序

通过JavaFX提供的JFXPanel,可以“较方便”地在Swing程序中使用JavaFX组件。如下代码演示如何在JFrame中显示一个使用JavaFX控件模拟的表单信息:

首先,构造一个4列10行的JavaFX GridPane:

package com.lirong.javafx.demo.j1002.swing;

import javafx.geometry.HPos;

import javafx.geometry.Insets;

import javafx.geometry.Pos;

import javafx.scene.control.Label;

import javafx.scene.control.TextField;

import javafx.scene.layout.ColumnConstraints;

import javafx.scene.layout.GridPane;

import javafx.scene.layout.Priority;

public class TestGridPaneUI extends GridPane {

public TestGridPaneUI() {

super();

initUI();

}

private void initUI() {

// 显示网格线

setGridLinesVisible(Boolean.TRUE);

// 设置间隔

setVgap(5);

setHgap(5);

setPadding(new Insets(5));

// 设置为4列

ColumnConstraints col1 = new ColumnConstraints();

col1.setPercentWidth(20);

ColumnConstraints col2 = new ColumnConstraints();

col2.setPercentWidth(30);

ColumnConstraints col3 = new ColumnConstraints();

col3.setPercentWidth(20);

ColumnConstraints col4 = new ColumnConstraints();

col4.setPercentWidth(30);

getColumnConstraints().addAll(col1, col2, col3, col4);

// 填充控件

int row = 0;

int col = 0;

while (row < 10) {

Label label01 = new Label(String.format("Row-%d,Col-%d:", row + 1, col + 1));

label01.setAlignment(Pos.CENTER_RIGHT);

// 右对齐

GridPane.setHalignment(label01, HPos.RIGHT);

add(label01, col, row);

col++;

if (col > 3) {

row++;

col = 0;

}

TextField text01 = new TextField();

// 填充整个单元格

GridPane.setHgrow(text01, Priority.ALWAYS);

add(text01, col, row);

col++;

if (col > 3) {

row++;

col = 0;

}

Label label02 = new Label(String.format("Row-%d,Col-%d:", row + 1, col + 1));

add(label02, col, row);

// 右对齐

GridPane.setHalignment(label02, HPos.RIGHT);

col++;

if (col > 3) {

row++;

col = 0;

}

TextField text02 = new TextField();

// 填充整个单元格

GridPane.setHgrow(text02, Priority.ALWAYS);

add(text02, col, row);

col++;

if (col > 3) {

row++;

col = 0;

}

}

}

}

然后把这个GridPane显示在JFrame中:

package com.lirong.javafx.demo.j1002.swing;

import javafx.embed.swing.JFXPanel;

import javafx.scene.Scene;

import javax.swing.JFrame;

public class TestSwingFX {

public static void main(String[] args) {

JFrame frame = new JFrame();

frame.setTitle("Swing JavaFX Test");

frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

frame.setSize(800, 600);

JFXPanel jfxPanel = new JFXPanel();

TestGridPaneUI gridPaneUI = new TestGridPaneUI();

jfxPanel.setScene(new Scene(gridPaneUI));

frame.add(jfxPanel);

frame.setVisible(Boolean.TRUE);

}

}

运行效果如下:

J1002.JavaFX简介

取消网格线后的效果:

J1002.JavaFX简介

关键类为JFXPanel,它是处理Swing和JavaFX界面、事件等交互的中介。

在拖放JFrame时,GridPanel将根据我们设置的比例自动缩放。