現在比較流行的桌面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按鈕窗口就不會被關閉。