现在比较流行的桌面gui框架有WPF、WinForm、Qt、javafx等。其中WPF和WinForm目前还只能在运行Winsows上。Qt(widget)是一个很强大的跨平台C++框架(不仅是UI),但用C++写界面实在有点蛋疼,且编译出来的体积很大。html
JavaFX是基于JAVA的开源桌面框架,笔者曾学习过Qt,打算尝试使用Java写桌面应用,如今网上关于JavaFX的教程不时不少,所以在这里记录一下学习过程。java
JavaFX11的环境不包括在JDK中,所以要在配置好JDK11的基础上单独配置,具体方法能够参考JavaFX官网。框架
新建工程,在Main.java
中输入下列代码:ide
public class Main extends Application { @Override public void start(Stage primaryStage) throws Exception{ VBox layout = new VBox(); Label label = new Label("Hello world"); layout.getChildren().add(label); Scene scene = new Scene(layout, 300, 300); primaryStage.setTitle("Hello World"); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }
JavaFX中的Stage
能够看做是窗口,Scene
是窗口中的内容,调用Stage
的setScene
函数来设置窗口内容,窗口能够在运行时切换显示的Scene
,实现Tab
页面的效果。函数
VBox
是JavaFX中的一种布局,其中的元素纵向排列,向VBox
中添加元素须要调用vbox.getChildren().add(control)
,如上所示。布局
构造Scene
时传入顶层的布局(相似Qt中QMainWindow
的CentralWidget
)及大小。最后调用show
函数将窗口显示出来。学习
控件(Control)是GUI框架中最重要的部分,也是用户与程序进行交互的媒介。ui
在JavaFX中使用控件须要导入包,例如.net
import javafx.scene.control.Label; import javafx.scene.control.*;
框架中不一样控件的使用方法大同小异,这里用最经常使用的按钮做为示例。code
构造一个Button
对象并添加到VBox
中:
Button button = new Button("Click me"); VBox layout = new VBox(); layout.getChildren().add(button); Scene scene = new Scene(layout, 300, 300); primaryStage.setScene(scene);
EventHandler
接口建立Handler
类实现EventHandler
接口
class Handler implements EventHandler<ActionEvent> { @Override public void handle(ActionEvent actionEvent) { if(actionEvent.getSource() instanceof Button) ((Button) actionEvent.getSource()).setText("Click me again"); } }
为按钮注册点击方法
button.setOnAction(new Handler());
Button
还有setOnMouseClicked
,setOnTouchPressed
等方法,这些是专门为处理鼠标事件及触摸事件,setOnAction
函数用来处理按钮触发事件(无论按钮被哪一种方式触发,具体参考文档)。
由代码能够得出,setOnAction
函数接收一个EventHandler
接口,接口的handle
方法用来处理事件。
与上一方法同理,咱们可使用匿名内部类建立接口
button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent actionEvent) { if(actionEvent.getSource() instanceof Button) ((Button) actionEvent.getSource()).setText("Click me again"); } });
Lambda
表达式Java
中的一些接口能够由lambda
表达式代替,所以能够在setOnAction
中传入lambda
表达式:
button.setOnAction(actionEvent -> { if(actionEvent.getSource() instanceof Button){ ((Button) actionEvent.getSource()).setText("Click me again"); } });
这样就能够在实现简单的事件处理器时没必要再特地实现接口。
使用其余控件的方法也都相似按钮,使用时能够查询文档,或者根据IDE的代码提示获知函数签名及使用方法。
在一个桌面程序中每每有多个窗口,下面介绍添加窗口的方法。
添加MsgBox
类
public class MsgBox { public static void show(String title) { Stage window = new Stage(); window.setTitle(title); Button trueButton = new Button("True"); Button falseButton = new Button("False"); HBox hBox = new HBox(10); //10为元素间空隙 hBox.getChildren().addAll(trueButton, falseButton); Scene scene = new Scene(hBox, 100, 100); window.setScene(scene); window.show(); } }
与主窗口建立过程相同,新建stage
、Scene
、布局及控件,最后使用Stage
的show
方法显示出来。
调用MsgBox
类的show
方法便可显示窗口,函数的参数为窗口的标题。
设置主窗口中的按钮事件,点击按钮后会显示一个MsgBox
窗口。
button.setOnAction(actionEvent -> MsgBox.show("SubWindow"));
Stage
对象可使用initModality
方法设置窗口模态类型
window.initModality(Modality.WINDOW_MODAL);
类型包括 Modality.NONE
, Modality.WINDOW_MODAL
, Modality.APPLICATION_MODAL
。
Modality.NONE
: 不阻塞任何窗口Modality.WINDOW_MODAL
: 窗口级别的模态,仅仅阻塞与对话框关联的窗口,用户能够正常访问其余窗口,适合用于多窗口的程序。Modality.APPLICATION_MODAL
(默认值): 应用程序级别的模态,窗口将阻塞整个程序,没法访问程序中其余的窗口有时咱们须要获得用户在子窗口中的操做,例如在本文的例子中,获知用户点了哪个按钮。
接下来实现这样的功能——点击True按钮就在控制台打印true
,不然打印'false'。
更改MsgBox
中的代码
public static boolean show(String title) { Stage window = new Stage(); window.setTitle(title); Button trueButton = new Button("True"); Button falseButton = new Button("False"); trueButton.setOnAction(actionEvent -> { answer = true; window.close(); }); falseButton.setOnAction(actionEvent -> { answer = false; window.close(); }); HBox hBox = new HBox(10); hBox.getChildren().addAll(trueButton, falseButton); Scene scene = new Scene(hBox, 100, 100); window.setScene(scene); window.showAndWait(); return answer; }
show
函数返回一个boolean
类型的值,这个值是由点击的按钮决定的,按钮点击后会关闭窗口,返回布尔值。
设置主窗口中按钮点击事件
button.setOnAction(actionEvent -> { var result = MsgBox.show("SubWindow"); System.out.println(result); });
showAndWait
函数这个函数会阻塞当前事件,直到窗口被关闭后才会返回,并执行接下类的语句。在上例中,咱们显示窗口并等待,直到点击按钮使窗口被关闭,才执行后面的return answer
语句。
能够尝试改成调用show
方法,观察返回的结果。
有时在用户关闭窗口时,须要执行必定的操做,例如保存设置、确认是否退出等。
这时咱们能够经过setOnCloseRequest
函数设置窗口关闭时触发的事件
window.setOnCloseRequest(windowEvent -> { System.out.println("The window will be closed!"); });
JavaFX
在关闭窗口时,首先执行这一事件处理函数,再将窗口关闭。但在某些状况下(例如确认是否关闭),咱们须要在处理事件时取消窗口的关闭,这种状况下能够调用windowEvent
的consume
方法,告诉事件系统,此事件已经被处理完毕,没必要再执行其余处理动做(如关闭窗口)。
将主窗口的代码改成:
@Override public void start(Stage primaryStage) throws Exception { Button button = new Button("Click me"); button.setOnAction(actionEvent -> { var result = MsgBox.show("SubWindow"); System.out.println(result); }); VBox layout = new VBox(); layout.getChildren().add(button); Scene scene = new Scene(layout, 300, 300); primaryStage.setScene(scene); primaryStage.setTitle("Hello World"); primaryStage.setOnCloseRequest(windowEvent -> { var result = MsgBox.show("Do you want to CLOSE?"); if (result == false) { windowEvent.consume(); } }); primaryStage.show(); }
当用户点击关闭按钮时,将会弹窗询问是否关闭,若用户点击False按钮窗口就不会被关闭。