這篇會使用 Java Package 與 JSS Package 改寫 HelloFX,並將 JavaFX Scene 拆分到獨立的 Lisp 檔案。完成後,主程式只負責建立 Application 和顯示 Stage,Scene 的內容則由另一個檔案建立。
ABCL與JAVA交互的方式主要是透過JAVA Package和JSS Package,這也是在package.lisp所引入的:
(defpackage :abcl-javafx
(:use :cl :java :jss)) ;; 將 :jss 先補上
像是我們在一開始申明的HelloFX運行時類別,所使用的#'jnew-runtime-class就是來自JAVA Package。
java:jnew-runtime-class can inject fully synthetic Java classes–and their objects– into the current JVM process whose behavior is specified via closures expressed in Common Lisp.
計畫將Scene的內容,另外規劃成一份檔案。現在將main.lisp調整成以下內容:
(in-package :abcl-javafx)
(defun +MY-APP.start+ (this stage)
(prog ((java-info-scene (create-java-info-scene)))
(#"setScene" stage java-info-scene)
(#"show" stage)))
(defconstant +MY-APP+
(jnew-runtime-class
"MyAPP"
:superclass +application+
:methods `(("start" :void (,+stage+)
+MY-APP.start+))
:access-flags '(:public)))
(jstatic "launch" +application+ +MY-APP+ (jnew-array "java.lang.String" 1))
主類別程序改成申明一個MyAPP的JAVA Runtime Class類別物件,並透過defconstant綁定到常數+MY-APP+。MyAPP同樣繼承javafx.application.Application。
因為ABCL本身可以算是直譯式的,並不一定要定義主類別的main方法,因此直接不定義,改直接在main.lisp透過jstatic執行launch的靜態方法。
jstatic同樣由JAVA Package提供,用來呼叫類別的靜態方法。需要明確指定尋找的類別和衍伸類別,然後是傳入靜態方法的參數。要運行MyAPP可以這麼使用: (jstatic "launch" +application+ +MY-APP+ (jnew-array "java.lang.String" 1))。
Invokes the static method METHOD on class CLASS with ARGS.
Scene的設定放到另一個檔案中,現在+MY-APP.start+方法只需要建立Scene,並顯示Stage就好。將(jcall "setScene" stage scene)和(jcall "show" stage)透過JSS Package提供的方式改寫:
(jcall "setScene" stage scene)
(jcall "show" stage)
↑
對應
↓
(#"setScene" stage scene)
(#"show" stage)
JSS的全名是「Java Simple Syntax」或「Java Syntax Sucks」。提供一個更簡單的語法使用JAVA Package,像是#"是JSS提供的一個讀取巨集(read-macro),可以簡化#'jcall的用法。其實#'jstatic也可以透過JSS改寫,不過方法必須是以Symbol型別傳入,像是launch可以這樣改寫:
(#"launch" (make-symbol +application+) +MY-APP+ (jnew-array "java.lang.String" 1))
將Scene拆分到scenes/java-info.lisp檔案。請先建立scenes目錄,再新增這份檔案:
(in-package :abcl-javafx)
(defun create-java-info-scene ()
(prog* ((label-java-version (new +label+ (format nil "Hello, Java ~A " (#"getProperty" 'java.lang.System "java.version"))))
(label-javafx-version (new +label+ (format nil "With JavaFX ~A" (#"getProperty" 'java.lang.System "javafx.version"))))
(box (new +VBox+ 50.5 (jnew-array-from-list "javafx.scene.Node" `(,label-java-version ,label-javafx-version))))
(result (new +scene+ box 640 480)))
(#"setAlignment" box (jfield "javafx.geometry.Pos" "CENTER"))
(return result)))
把布局(layout)由StackPane改成VBox,JAVA版本資訊和JavaFX版本資訊分別建立Label物件加入VBox。再透過setAlignment設定中央對齊。
如此一來,就再進一步將Scene的程式拆出來,並透過JSS Package簡化JAVA Package使用方式。更新後的ASDF檔案如下:
(defsystem "abcl-javafx"
:description "Create GUI Application with ABCL and JavaFX"
:version "0.0.1"
:author "lagagain <lagagain@email.com>"
:licence "MIT"
:depends-on (#:jss)
:serial t
:components ((:file "package")
(:file "constants")
(:file "scenes/java-info") ;; 將Scene程式碼拆分出來
(:file "main")))
重新載入abcl-javafx系統:
abcl --load run-system.lisp --eval '(quit)'
執行後應該會看到置中的 Java 與 JavaFX 版本資訊,並且分成上下兩行顯示。這表示 Scene 已經由獨立檔案建立,主程式也成功透過 JSS 呼叫 JavaFX 方法。

下一篇將在這個 Scene 的基礎上加入 CSS,使用外部樣式檔案調整 JavaFX 元件的外觀。