JavaFX 除了用來顯示訊息的 Alert,也提供讓使用者選擇項目的 ChoiceDialog,以及輸入文字的 TextInputDialog。使用 showAndWait() 後,記得檢查回傳的 Optional,確認使用者是否有完成操作。
在Hello Button & Action使用了#'make-messagebox建立一個顯示訊息的對話框:
(defun make-messagebox (msg)
(new +Alert+ +AlertType.INFORMATION+
msg
(jarray-from-list
`(,+ButtonType.OK+))))
但因為當時並不在意使用者按下了什麼按鈕,或是直接關閉對話框,所以直接使用show()顯示對話框:
(prog ((alert (make-messagebox "Clicked Button")))
(#"show" alert))
這篇先來看看如何得知使用者按下了哪個按鈕。為了方便嘗試,直接使用發揮ABCL威力建立ABCL-EVAL場景,並切換到:abcl-javafx Package。
(in-package :abcl-javafx)

showAndWait() 取得回應原本我們是用show():
(#"show" (make-messagebox "hello"))
但是不管操作什麼回傳的結果都是NIL。

換成showAndWait():
(#"showAndWait" (make-messagebox "hello"))
現在會根據使用者按下OK或直接關閉對話框,回傳不同結果:Optional中包含ButtonType.OK,或回傳Optional.empty。

Alert建構子允許加入不同類型的按鈕:
(#"showAndWait" (new +Alert+
(jfield +AlertType+ "INFORMATION")
"嘗試不同的Alert"
(jarray-from-list `(,(jfield +ButtonType+ "OK")
,(jfield +ButtonType+ "CANCEL")
,(jfield +ButtonType+ "FINISH")
,(jfield +ButtonType+ "NEXT")
,(jfield +ButtonType+ "YES")
,(jfield +ButtonType+ "NO")
,(jfield +ButtonType+ "APPLY")
,(jfield +ButtonType+ "PREVIOUS")
,(jfield +ButtonType+ "CLOSE")))))


Alert是javafx.scene.control.Dialog<ButtonType>的子類別。建立Alert時傳入Alert.AlertType,JavaFX會根據類型預先設定標題、標頭文字、圖示和適合的按鈕。官方API提供以下幾種類型:
| 類型 | 適合的用途 | 預設按鈕 |
|---|---|---|
INFORMATION |
一般資訊或操作成功 | OK |
WARNING |
可能造成問題的提醒 | OK |
ERROR |
操作失敗或例外錯誤 | OK |
CONFIRMATION |
要求使用者確認操作 | OK、CANCEL |
NONE |
不指定預設的視覺類型 | 依建構方式而定 |
不過我不太確定這是因為在ABCL中使用的關係,還是有哪裡修改了風格;設定不同的AlertType時,外觀差異並不大。相較之下,從Java實作的CalcFX中,Alert風格就豐富許多。

除了Alert(MessageBox)以外,JavaFX還提供選擇對話框(ChoiceDialog)和文字輸入對話框(TextInputDialog),分別用來讓使用者選擇項目或輸入文字。
先來看看選擇對話框。要讓使用者從幾個選項中選一個,可以使用ChoiceDialog<T>。它的選項參數是Java的Collection,因此這裡先建立ArrayList再加入選項:
(defvar *color-options*
(let* ((c (new "java.util.ArrayList")))
(#"add" c "紅色")
(#"add" c "綠色")
(#"add" c "藍色")
c))
同樣要在
:abcl-javafx裡評估。(in-package :abcl-javafx)
然後建立ChoiceDialog,並提供*color-options*做為選項清單,以「綠色」做為預設選項:
(let* ((+ChoiceDialog+ "javafx.scene.control.ChoiceDialog")
(dialog (new +ChoiceDialog+ "綠色" *color-options*)))
(#"setTitle" dialog "選擇顏色")
(#"setHeaderText" dialog "請選擇一種顏色")
(#"setContentText" dialog "顏色:")
(#"showAndWait" dialog))
和Alert一樣,回傳值是Optional類別,需要先判斷使用者是否真的選擇了值。改寫如下:
(let* ((+ChoiceDialog+ "javafx.scene.control.ChoiceDialog")
(dialog (new +ChoiceDialog+ "綠色" *color-options*)))
(#"setTitle" dialog "選擇顏色")
(#"setHeaderText" dialog "請選擇一種顏色")
(#"setContentText" dialog "顏色:")
- (#"showAndWait" dialog))
+ (let ((result (#"showAndWait" dialog)))
+ (when (#"isPresent" result)
+ (#"get" result))))
在ABCL中建立的ChoiceDialog外觀不太理想,因此這裡附上官方文件中的範例畫面:

不同於前兩者,Alert用按鈕提供選項;ChoiceDialog提供下拉式選單,文字輸入對話框則是由使用者自行輸入內容回覆。所以如果對話框的目的不是通知、也不是選項選擇,而是取得一段文字,使用TextInputDialog是一個簡單的方式:
;;(let ()
(defconstant +TextInputDialog+
"javafx.scene.control.TextInputDialog")
(defun ask-name ()
(let ((dialog (new +TextInputDialog+ "Anonymous")))
(#"setTitle" dialog "輸入名稱")
(#"setHeaderText" dialog "建立使用者")
(#"setContentText" dialog "名稱:")
(let ((result (#"showAndWait" dialog)))
(when (#"isPresent" result)
(#"get" result)))))
(ask-name)
;;)
註解掉的兩行
(let ()、),是為了讓ABCL-EVAL將其視為整個評估形式(form),一次評估並保留最後形式的結果做為回傳值。
也可以一段一段的輸入到ABCL-EVAL去執行,結果也是會相同的。

以上介紹了JavaFX提供的三種常用對話框:通知或確認訊息用Alert,從清單選值用ChoiceDialog,取得文字輸入則用TextInputDialog。需要讀取使用者的選擇或輸入時,使用showAndWait()並檢查Optional是否有值;接著可以試著將其中一種對話框接到自己的 JavaFX 操作流程中。