彈出窗口與選擇器(一)

在第8章中,我們瞭解了頂層容器,例如JFrame與JApplet。另外,我們探討了用來創建彈出窗口來顯示信息或是獲取用戶輸入的JDialog類。儘管JDialog類可以工作得很好,Swing組件集合同時提供了一些更爲簡單的方法來由彈出窗口獲取用戶輸入,我們將會在本章進行探討。

JOptionPane類對於顯示信息,獲取文本用戶輸入,或是獲取問題答案十分有用。ProgressMonitor與ProgressMonitorInputStream類可以使得我們監視長時間任務的過程。另外,JColorChooser與JFileChooser類提供了特性彈出窗口用來由用戶獲取顏色選擇,或是獲取文件或目錄名。通過使用這些類,我們的界面開發任務可以更爲快速與簡單的實現。

9.1 JOptionPane Class

JOptionPane是一個可以用來創建放在彈出窗口中的面板的一個特殊類。面板的目的是向用戶顯示信息或是由用戶獲取響應。要實現這一任務,面板在四個區域顯示內容(如圖9-1):

  • Icon:圖標區域用來顯示一個圖標,標識顯示給用戶的信息類型。爲特定的消息類型提供默認的圖標是所安裝的觀感的責任,但是如果我們希望顯示其他的圖標類型,我們可以提供我們自己的圖標。
  • Message:這一區域的基本目的是顯示一個文本信息。另外,這一區域還可以包含其他的可選對象集合來使得消息提供更多的信息。
  • Input:輸入區域允許用戶提供消息的響應。響應可以是自由格式,文本域,或組合框中選擇列表或是列表控件。要顯示是或否類型的問題,應該使用按鈕區域。
  • Button:按鈕區域也可以用來獲取用戶輸入。在這個區域的按鈕選擇通知了JOptionPane使用的結束。可以使用按鈕標籤的默認集合,或者我們可以顯示任意數量的按鈕,也可以沒有,並使用我們所希望的標籤。

Swing_9_1

所有這些區域都是可選的(儘管沒有消息與按鈕的面板使得選項面板毫無用處)。

除了作爲一個在彈出窗口中具有四部分的面板以外,JOptionPane還具有自動將其自身放在一個彈出窗口中並管理用戶響應的獲取的能力。依據我們所提供給用戶的GUI類型,他也可以將自身放在一個JDialog或是一個JInternalFrame中。藉助於Icon與JButton組件集合,JOptionPane可以很容易配置來顯示多種消息與輸入對話框。

注意,因爲JOptionPane可以自動將其自身放在一個JDialog中,所以我們並不需要直接創建JDialog。

9.1.1 創建JOptionPane

我們可以使用JOptionPane的七個構造函數或是使用本章稍後將會討論的25個工廠方法來創建一個JOptionPane。當手動創建JOptionPane時,我們擁有最大的控制。然而,然後我們必須將其放在一個彈出窗口中,顯示窗口,最後管理響應。

由於自動完成所有事情所提供的方法的簡便,我們也許會認爲當使用JOptionPane時應只使用工廠方法。然而,通過本章,我們將會發現手動完成一些事情的其他原因。另外,當我們使用一個可視化編程環境時,環境會將JOptionPane看作一個JavaBean組件,並且會忽略其工廠方法。

對於七個構造函數,我們可以有6個不同參數的不同組合。參數可以使得我們配置圖9-1中所顯示的不同區域的一部分。六個參數是消息,消息類型,可選類型,圖標,選項數組,以及初始的選項設置。參數的用法與工廠方法相同。

我們首先來看一下七個構造函數,然後探討不同的參數。注意構造參數是級聯的,並且只在前一個構造函數中添加額外的參數。

public JOptionPane()
JOptionPane optionPane = new JOptionPane();
 
public JOptionPane(Object message)
JOptionPane optionPane = new JOptionPane("Printing complete");
 
public JOptionPane(Object message, int messageType)
JOptionPane optionPane = new JOptionPane("Printer out of paper",
  JOptionPane.WARNING_MESSAGE);
 
public JOptionPane(Object message, int messageType, int optionType)
JOptionPane optionPane = new JOptionPane("Continue printing?", 
  JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION);
 
public JOptionPane(Object message, int messageType, int optionType,
  Icon icon)
Icon printerIcon = new ImageIcon("printer.jpg");
JOptionPane optionPane = new JOptionPane("Continue printing?", 
  JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION, printerIcon);
 
public JOptionPane(Object message, int messageType, int optionType, Icon icon,
  Object options[ ])
Icon greenIcon = new DiamondIcon(Color.GREEN);
Icon redIcon = new DiamondIcon(Color.RED);
Object optionArray[] = new Object[] { greenIcon, redIcon} ;
JOptionPane optionPane = new JOptionPane("Continue printing?", 
  JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION, printerIcon,
  optionArray);
 
public JOptionPane(Object message, int messageType, int optionType, Icon icon,
  Object options[], Object initialValue)
JOptionPane optionPane = new JOptionPane("Continue printing?", 
  JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION, printerIcon,
  optionArray, redIcon);

JOptionPane消息參數

message參數是一個Object,而不是一個String。當我們通常將一個引起來的字符串作爲Object的參數時,我們僅僅可以在消息區域顯示我們希望顯示的任何內容。在本章稍後的「理解消息屬性」一節中,我們將會了解這一參數的高級用法。然而,概括來說,有四個基本的規則可以解釋Object類型消息參數意義。對於Object中的元素,依次遵循下列規則:

  • 如果消息是一個對象數組(Object[]),會使得JOptionPane在單獨行放置每一項。
  • 如果消息是一個Component,則將該組件放置在消息區域。
  • 如果消息是一個Icon,會在JLable中放置Icon,並且在消息區域顯示標籤。
  • 如果消息是一個Object,使用toString()方法將其轉換爲String,將String放在JLabel中,並在消息區域顯示標籤。

JOptionPane消息類型與圖標參數

messageType構造函數參數用來表示顯示在JOptionPane中的消息類型。如果我們沒有爲JOptionPane指定自定義的圖標,已安裝的觀感使用messageType參數設置來決定在圖標區域使用哪個圖標。JOptionPane提供了五個不同的消息類型常量:

  • ERROR_MESSAGE用來顯示一個錯誤消息
  • INFORMATION_MESSAGE用來顯示一個信息提示消息
  • QUESTION_MESSAGE用來顯示一個查詢消息
  • WARNING_MESSAGE用來顯示一個警告消息
  • PLAIN_MESSAGE用來顯示任何其他類型的消息

如果我們使用了同時帶有messageType與icon作爲參數的構造函數,並且希望JOptionPane爲messageType使用默認圖標,只需要將icon參數的值指定爲null。如果icon參數不爲null,則會使用所指定的圖標,而不論是何種消息類型。

如果沒有指定構造函數的messageType參數,則默認的消息類型爲PLAIN_MESSAGE。

JOptionPane選項類型參數

optionType構造函數參數用來決定按鈕區域的按鈕集合配置。如果提供了一個下面所描述的options參數,則optionType參數會被忽略,而按鈕集合配置則會由options獲取。JOptionPane有四個不同的選項類型常量:

  • DEFAULT_OPTION用於一個OK按鈕
  • OK_CANCEL_OPTION用來OK與Cancel按鈕
  • YES_NO_CANCEL_OPTION用於Yes,No與Cancel按鈕
  • YES_NO_OPTION用於Yes與No按鈕

如果沒有指定optionType構造函數參數,則默認的選項類型爲DEFAULT_OPTION。

JOptionPane選項以及初始值參數

options參數是一個用來構建用在JOptionPane按鈕區域的JButton對象集合的Object數組。如果這個參數爲null(或者是使用了一個沒有這個參數的構造函數),則按鈕標籤會由optionType參數來決定。否則,這個數組的作用類似於消息參數,但是並不支持迭代數組:

  • 如果options數組元素是一個Component,則會將這個組件放在按鈕區域。
  • 如果options數組元素是一個Icon,則會將這個Icon放在一個JButton中,然後將按鈕放在按鈕區域。
  • 如果options數組元素是一個Object,則使用toString()方法將其轉換爲一個String,將這個String放在一個JButton中,然後將按鈕放在按鈕區域。

通常,options參數是一個String對象的數組。也許我們希望JButton上帶有一個Icon,儘管最終的按鈕不會帶有標籤。如果我們希望按鈕上同時帶有圖標與文本標籤,我們可以手動創建一個JButton,並將其放在一個數組中。相對的,我們可以在數組中直接包含其他任意的Component。然而對於後兩種方法有一個小問題。我們要負責處理組件選中的響應,並且通知JOptionPane用戶何時選擇了這個組件。本章稍後的「向按鈕區域添加組件」將會討論如何正確處理這種行爲。

當options參數不爲null時,initialValue參數可以指定當面板初始顯示時哪一個按鈕是默認按鈕。如果其爲null,則按鈕區域的第一個組件爲默認按鈕。在任何一種情況下,第一個按鈕會獲得輸入焦點,除非在消息區域有一個輸入組件,在這種情況下,輸入組件會獲得初始輸入焦點。

9.1.2 顯示JOptionPane

在我們使用一個構造函數創建了JOptionPane之後,我們所獲得的是一個使用組件填充的面板。換句話說,所獲得的JOptionPane還沒有位於彈出窗口中。我們需要創建一個JDialog,一個JinternalFrame,或是其他的彈出窗口,然後將JOptionPane放在其中。另外,如果我們選擇JOptionPane構造的這種手動風格,我們需要處理彈出窗口的關閉。我們必須監聽按鈕區域組件的選中,然後在選中之後隱藏彈出窗口。

因爲在這裏有如此多的工作要做,JOptionPane包含兩個助手方法來將JOptionPane放在一個JDialog或是一個JInternalFrame之中,並且處理前面所描述的所有行爲:

public JDialog createDialog(Component parentComponent, String title)
public JInternalFrame createInternalFrame(Component parentComponent, String title)

注意,當使用createDialog()與createInternalFrame()方法創建彈出窗口時,自動創建的按鈕的選中會導致所創建的彈出窗口的關閉。然後我們需要使用getValue()方法來查詢JOptionPane用戶選中的選項,而且如果需要,可以使用getInputValue()方法獲得輸入值。

方法的第一個參數是彈出窗口所在的組件。第二個參數是彈出窗口的標題。一旦我們創建了彈出窗口,無論他是一個JDialog還是一個JInternalFrame,我們要顯示他。然後彈出窗口會在按鈕區域的一個組件被選中之後關閉,此時,我們的程序繼續。下面的代碼行演示了顯示在一個JDialog中的JOptionPane的創建。所創建的彈出窗口顯示在圖9-2中。

JOptionPane optionPane = new JOptionPane("Continue printing?",
  JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION); 
JDialog dialog = optionPane.createDialog(source, "Manual Creation");
dialog.setVisible(true);

Swing_9_2

我們創建了JOptionPane,將其放在一個彈出窗口中,顯示彈出窗口,並且用戶響應之後,我們需要確定用戶的選擇是什麼。選擇是通過JOptionPane的public Object getValue()方法來提供的。getValue()方法返回的值是通過是否向JOptionPane構造函數提供了一個options數組來確定的。如果我們提供了這個數組,則會返回選中的參數。如果我們沒有提供數組,則會返回一個Integer對象,而其值表示了選中的按鈕在按鈕區域中的位置。在另一種情況下,如果沒有選擇任何內容,getValue()方法會返回null,例如當通過選擇彈出窗口標題的相應裝飾關閉JDialog時。

爲了更容易獲取這種多面響應,列表9-1顯示了一個OptionPaneUtils類,其中定義了方法public static int getSelection(JOptionPane optionPane)。指定一個選項面板,這個方法會將選中值的位置作爲int返回,而不論是否提供了選項數組。爲了標識沒有選中任何內容,JOptionPane.CLOSED_OPTION(-1)會被返回。

package swingstudy.ch09;
 
import javax.swing.JOptionPane;
 
public final class OptionPaneUtils {
 
	private OptionPaneUtils() {
 
	}
 
	public static int getSelection(JOptionPane optionPane) {
		// Default return value, signals nothing selected
		int returnValue = JOptionPane.CLOSED_OPTION;
 
		// Get selected value
		Object selectedValue = optionPane.getValue();
		// if none, then nothing selected
		if(selectedValue != null) {
			Object options[] = optionPane.getOptions();
			if(options == null) {
				// default buttons, no array specified
				if(selectedValue instanceof Integer) {
					returnValue = ((Integer)selectedValue).intValue();
				}
			}
			else {
				// array of option buttons specified
				for(int i=0, n=options.length; iif(options[i].equals(selectedValue)) {
						returnValue = i;
						break; // out of for loop
					}
				}
			}
		}
		return returnValue;
	}
}

藉助於這個新的OptionPaneUtils.getSelection(JOptionPane)助手方法,現在我們可以使用一行代碼來確定選項面板的選擇,並依據響應執行動作。

int selection = OptionPaneUtils.getSelection(optionPane);
switch (selection) {
  case ...: ...
    break;
  case ...: ...
    break;
  default: ...
}

如果我們使用一個null選項數組創建一個JOptionPane,我們可以使用JOptionPane類中的常量來標識默認按鈕標籤的位置以及由OptionPaneUtils.getSelection(JOptionPane)方法返回的值。這些常量列在表9-1中。使用這些常量可以使用我們避免硬編碼常量,例如0,1,2或是-1。

Swing_table_9_1

9.1.3 在彈出窗口中自動創建JOptionPane

我們可以手動創建JOptionPane,將其放在一個JDialog或是JInternalFrame中,並獲取響應。相對的,我們可以使用JOptionPane工廠方法在JDialog或是JInternalFrame中直接創建JOptionPane組件。使用工廠方法,我們可以使用一行代碼創建一個選項面板,將其放在一個彈出窗口中,並且獲取響應。

有25個工廠方法,可以分爲兩類:創建顯示在JDialog中的JOptionPane或是創建顯示在JInternalFrame中的JOptionPane。在JInternalFrame中顯示JOptionPane的方法以showInternalXXXDialog()的方式命名,而創建顯示在JDialog中的面板則以showXXXDialog()的方式命名。

JOptionPane的工廠方法的第二個分組是填充方法名字中的XXX部分。這表示了我們可以創建並顯示的選項面板的各種消息類型。另外,消息類型定義了用戶在選項面板中選擇某個組件之後所返回的內容。四個不同的消息類型如下:

  • Message:對於消息彈出窗口,沒有返回值。所以其方法定義爲void show[Internal]MessageDialog(...)。
  • Input:對於輸入彈出窗口,返回值或者是用戶在文本域中所輸入的內容(String),或者是用戶在選項列表中的選擇(Object)。所以,依據我們所使用的版本,show[Internal]InputDialog(...)方法或者返回一個String,或者返回一個Object。
  • Confirm:對於確認彈出窗口,返回值標識了用戶在選項面板內選擇的按鈕。在一個按鈕被選中後,彈出窗口消失,而返回值是顯示在表9-1中的整數常量中的一個。所以,在這裏方法定義爲int show[Interal]ConfirmDialog(...)。
  • Option:對於選項彈出窗口,返回值是一個int,與確認彈出窗口的返回值類型相同,所以方法定義爲itn show[Internal]OptionDialog(...)。如果按鈕標籤是通過一個非null的參數手動指定的,整數表示所選擇的按鈕的位置。

表9-2中的信息應該可以幫助我們理解25個方法及其參數。方法名(與返回類型)顯示在表的左側,而其參數列表(與數據類型)顯示在右側。對於每個方法名跨越各個列重複出現的數字標識了此方法的一個特定的參數集合。例如,showInputDialog行在父組件列,消息列,標題列以及消息類型列顯示了一個3.所以,showInputDialog方法的一個版本定義如下:

public static String showInputDialog(Component parentComponent, Object message, String title, int messageType)

由於定義了不同的showXXXDialog()方法,我們不再需要親自確定所選中的按鈕或是用戶的輸入。依據所顯示的對話框類型,各種方法的返回值是下列值中的一個:無返回值(void返回類型),表9-1中的int,String或是Object。

Swing_table_9_2

工廠方法的JOptionPanl參數

幾乎工廠方法的所有參數都匹配JOptionPane的構造函數參數。本章前面的「創建JOptionPane」中的兩個列表描述了消息類型與選項類型參數的可接受的值。另外,同時描述了消息,選項以及初值參數用法。父組件以及標題參數被傳遞給createDialog()或是createInternalFrame()方法,這依賴於JOptionPane所嵌入的彈出窗口的類型。

接下來我們需要考慮的是showInputDialog()方法的選擇值參數以及初始選中值參數。對於輸入對話框,我們可以向用戶要求文本輸入,並且允許用戶輸入任何內容,或者是我們可以向用戶展示一個預定義的選項列表。showInputDialog()的選擇值參數決定了我們如何提供該選項集合。初始選擇值表示當JOptionPane首次顯示時被選擇的特定項。觀感將會依據所展示的選項數目來決定義要使用的相應Swing組件。對於較小的列表,可以使用JComboBox。對於大的列表,對於Motif,Metal/Ocean以及Windows觀感,多於20將會使用JList。

Swing_table_9_3

消息彈出窗口

sowMessageDialog()與showInternalDialog()方法使用彈出標題「Message」創建一個INFORMATION_MESSAGE彈出窗口,除非我們爲消息類型與窗口標題指定了不同的參數設置。因爲消息對話框的目的就是要顯示一個信息,這些對話框只提供了OK按鈕,並且沒有返回值。圖9-3顯示了使用下面的代碼所創建的示例消息彈出窗口:

JOptionPane.showMessageDialog(parent, "Printing complete"); JOptionPane.showInternalMessageDialog(desktop, "Printing complete");

Swing_9_3

確認彈出窗口

showConfirmDialg()與showInternalConfirmDialog()方法默認情況下使用QUESTION_MESSAGE類型以及「Select an Option」彈出標題創建一個確認彈出窗口。因爲確認對話框詢問一個問題,其默認選項類型爲YES_NO_CANCEL_OPTION,爲其指定Yes,No以及Cancel按鈕。對這些方法的調用所獲得的返回值是下列JOptionPane常量中的一個:YES_OPTION,NO_OPTION或是CANCEL_OPTION。我們可以很容易猜到哪一個常量對應哪一個按鈕。圖9-4顯示了使用下面代碼創建的確認彈出窗口:

JOptionPane.showConfirmDialog(parent, "Continue printing?");
JOptionPane.showInternalConfirmDialog(desktop, "Continue printing?");

Swing_9_4

輸入彈出窗口

默認情況下,showInputDialog()與showInternalInputDialog()方法使用「Input」彈出標題創建一個QUESTION_MESASGE彈出窗口。輸入對話框的選項類型爲OK_CANCEL_OPTION,爲其指定一個OK與一個Cancel按鈕,而且選項類型是不可以改變的。這些方法的返回數據類型或者是一個String,或者是一個Object。如果我們沒有指定選項值,彈出窗口會向用戶展示一個文本域,並且將輸入作爲一個String返回。如果我們指定了選項值,我們會由選項值數組中獲取一個Object。圖9-5顯示了使用下面代碼所創建的輸入彈出窗口:

JOptionPane.showInputDialog(parent, "Enter printer name:");
// Moons of Neptune
String smallList[] = { 
  "Naiad", "Thalassa", "Despina", "Galatea", "Larissa", "Proteus",
  "Triton", "Nereid"} ;
JOptionPane.showInternalInputDialog(desktop, "Pick a printer", "Input",
  JOptionPane.QUESTION_MESSAGE, null, smallList, "Triton");
// Twenty of the moons of Saturn
String bigList[] = {"Pan", "Atlas", "Prometheus", "Pandora", "Epimetheus",
  "Janus", "Mimas", "Enceladus", "Telesto", "Tethys", "Calypso", "Dione",
  "Helene", "Rhea", "Titan", "Hyperion", "Iapetus", "Phoebe", "Skadi",
  "Mundilfari"};
JOptionPane.showInputDialog(parent, "Pick a printer", "Input",
  JOptionPane.QUESTION_MESSAGE, null, bigList, "Titan");

Swing_9_5

選項彈出窗口

showOptionDialg()與showInternalOptionDialog()方法提供了最大的靈活性,因爲他們允許我們所有的參數。他們沒有默認參數,並且返回值是一個int。如果沒有指定options參數,則返回值爲表9-1中所列出的常量之一。否則,返回值表示了所選擇的選項在options參數中的組件位置。圖9-6顯示了使用下列代碼創建的多個輸入彈出窗口,其中在按鈕上提供了圖標(而不是文本):

Icon greenIcon = new DiamondIcon(Color.GREEN);
Icon redIcon = new DiamondIcon(Color.RED);
Object iconArray[] = { greenIcon, redIcon} ;
JOptionPane.showOptionDialog(source, "Continue printing?", "Select an Option",
  JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, iconArray,
  iconArray[1]);
Icon blueIcon = new DiamondIcon(Color.BLUE);
Object stringArray[] = { "Do It", "No Way"} ;
JOptionPane.showInternalOptionDialog(desktop, "Continue printing?",
  "Select an Option", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
blueIcon, stringArray, stringArray[0]);

Swing_9_6

9.1.4 JOptionPane屬性

表9-3顯示了JOptionPane的15個屬性。這些屬性只有在我們沒有使用JOptionPane的工廠方法時纔可以訪問。對於大多數參數來說,其意義直接與一個構造函數參數相對應。

Swing_table_9_5

對於輸入對話框或是當selectionValues屬性不爲null時,wantsInput屬性會自動被設置爲true。inputValue屬性是由一個輸入對話框中選擇的項。value屬性標識了由按鈕區域所做的選項。

顯示多行消息

默認情況下maxCharacterPerLineCount屬性設置爲一個極大的值,Integer.MAX_VALUE。由於某些奇怪的原因,Swing開發選擇不爲這個屬性提供一個setter方法。如果我們需要修改這個屬性,我們必須繼承JOptionPane並且重寫public int getMaxCharacterPerLineCount()方法。這使得一個長的文本被分爲選項面板中的多行。另外,我們不能使用任何的工廠方法,因爲他們並不知道我們的子類。

爲了幫助我們創建短小的JOptionPane組件,我們可以將列表9-2中所示的代碼添加到前面列表9-1中所示的OptionPaneUtils類義中。這個新方法提供了指定所需要的選項面板字符寬度的方法。

public static JOptionPane getNarrowOptionPane(int maxCharacterPerLineCount) {
		// our inner class definition
		class NarrowOptionPane extends JOptionPane {
			int maxCharacterPerLineCount;
 
			NarrowOptionPane(int maxCharacterPerLineCount) {
				this.maxCharacterPerLineCount = maxCharacterPerLineCount;
			}
 
			public int getMaxCharacterPerLineCount() {
				return maxCharacterPerLineCount;
			}
		}
		return new NarrowOptionPane(maxCharacterPerLineCount);
	}

一旦定義了這個方法與新類,我們可以創建指定字符寬度的選項面板,手動配置所有的屬性,將其放在一個彈出窗口中,顯示彈出窗口,然後確定用戶的響應。下面的代碼演示了使用這個新的功能:

String msg = "this is a really long message ... this is a really long message";
JOptionPane optionPane = OptionPaneUtils.getNarrowOptionPane(72);
optionPane.setMessage(msg);
optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
JDialog dialog = optionPane.createDialog(source, "Width 72");
dialog.setVisible(true);

圖9-7顯示了我們沒有修改maxCharacterPerLineCount屬性時對話框的樣子。圖9-7同時顯示新的短小JOptionPane的樣子。

Swing_9_7

儘管這看起來需要大量的工作,這卻是創建多行選項面板的最好方法,除非我們要手動將消息分析爲單行。

理解消息屬性

在本章前面所有使用JOptionPane構造函數的消息參數以及使用工廠方法的例子中,消息僅是一個字符串。正如在前面「JOptionPane消息參數」一節中所描述的,這個參數並不需要是一個字符串。例如,如果這個參數是一個字符串數組,每一個字符串就會在單獨的行上顯示。這就減少了使用短小JOptionPane的必要,但是需要我們自己計算字符。然而,因爲我們是在分割消息,我們可以使用25個廠方法中的一個。例如,下面的代碼創建了圖9-8中顯示的彈出窗口。

String multiLineMsg[] = { "Hello,", "World"} ;
JOptionPane.showMessageDialog(source, multiLineMsg);

Swing_9_8

消息參數不僅僅支持顯示字符串數組,同時他還可以支持任意對象類型的數組。如果數組中的元素是一個Component,他會被直接放置在消息區域中。如果其元素是一個Icon,圖標會被放置在一個JLabel中,然後JLabel被放置在消息區域。所有其他的對象會被轉換爲一個String,將其放在一個JLabel中,並且在消息區域顯示,除非對象本身是一個數組;在這種情況下,規則會被迭代應用。

爲了演示這種可能性,圖9-9顯示了JOptionPane的真正功能。實際的內容並不是要顯示特定的內容,只是爲了表明我們可以顯示多種不同的內容。消息參數是由下面的數組構成的:

Object complexMsg[] = { 
   "Above Message", new DiamondIcon(Color.RED), new JButton("Hello"),
  new JSlider(), new DiamondIcon(Color.BLUE), "Below Message"} ;

Swing_9_9

向消息區域添加組件

如果我們要顯示圖9-9中的彈出窗口,我們就會注意到一個小問題。選項面板並不瞭解所嵌入的JSlider的設置,這與他自動了解JTextField,JComboBox或是JList組件的輸入不同。如果我們希望JOptionPane獲取JSlider值,我們需要使得我們的輸入組件修改JOptionPane的inputValue屬性。當這個值被修改時,選項面板會通知彈出窗口關閉,因爲JOptionPane已經獲取其輸入值。

將一個ChangeListener關聯到JSlider組件可以使得我們確定其值何時發生變化。向前面的列表9-1中顯示的OptionPaneUtils類中添加另一個方法可以使得我們更爲容易的重用JOptionPane中的這個特殊的JSlider。在列表9-3中以粗體顯示了重要的方法調用。相似的代碼行需要添加到我們希望在JOptionPane中使用的任意輸入組件上。這一行會在用戶修改了輸入組件的值時通知選項面板。

public static JSlider getSlider(final JOptionPane optionPane) {
		JSlider slider = new JSlider();
		slider.setMajorTickSpacing(10);
		slider.setPaintTicks(true);
		slider.setPaintLabels(true);
		ChangeListener chageListener =  new ChangeListener() {
			public void stateChanged(ChangeEvent event) {
				JSlider theSlider = (JSlider)event.getSource();
				if(!theSlider.getValueIsAdjusting()) {
					optionPane.setInputValue(new Integer(theSlider.getValue()));
				}
			}
		};
		slider.addChangeListener(chageListener);
		return slider;
	}

現在創建了這個特殊的JSlider,我們需要將其放置在一個JOptionPane中。這需要我們後動創建JOptionPane組件,奇怪的是,並不要求wantsInput屬性的設置。只有當我們希望JOptionPane來提供其自己的輸入組件時,wantsInput屬性纔會被設置爲true。因爲我們正在提供這樣的組件,所以就不需要這個屬性。最終彈出窗口顯示在圖9-10中。

JOptionPane optionPane = new JOptionPane();
JSlider slider = OptionPaneUtils.getSlider(optionPane);
optionPane.setMessage(new Object[] { "Select a value: " , slider} );
optionPane.setMessageType(JOptionPane.QUESTION_MESSAGE);
optionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
JDialog dialog = optionPane.createDialog(source, "My Slider");
dialog.setVisible(true);
System.out.println ("Input: " + optionPane.getInputValue());

Swing_9_10

注意,如果用戶並沒有移動滑塊,JOptionPane.getInputValue()會返回JOptionPane.UNINITIALIZED_VALUE。

向按鈕區域添加組件

在本章前面的「JOptionPane選項以及初始值參數」一節中,如果我們在JOptionPane的選項數組中有一個Component,我們必須自己配置組件來處理選中。對於我們通過options屬性添加其他組件也是如此。當組件被配置爲處理選中時,當組件被選中時,JOptionPane被嵌入的彈出窗口會顯示。按鈕的默認設置如此工作。當安裝我們自己的組件時,我們必須通過設置選項面板的value屬性在一個組件被選中時通知選項面板。

爲了演示這種機制,創建一個可以放置在選項中具有圖標與文本標籤的JButton。如果我們沒有自己定義這個組件,選項面板僅支持按鈕上標籤或是圖標的顯示。當按鈕被選中時,按鈕會通過將選項面板的value屬性設置爲按鈕當前的文本標籤來通知選項面板他被選中。在前面的列表9-1中添加另一個方法來使用我們可以創建一個這樣的按鈕。列表9-4源碼中以粗體顯示的代碼行是我們需要添加到其他我們希望與組件數組結合作爲JOptionPane的選項屬性的類似組件上的重要方法調用。這一行代碼會在這個組件被選中後調用。

public static JButton getButton(final JOptionPane optionPane, String text, Icon icon) {
		final JButton button = new JButton(text, icon);
		ActionListener actionListener = new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				// return current text label, instead of argument to method
				optionPane.setValue(button.getText());
			}
		};
		button.addActionListener(actionListener);
		return button;
	}

在創建了這個特殊的JButton之後,我們需要將其放在一個JOptionPane中。不幸的,這也需要較長的JOptionPane使用。最終的彈出窗口顯示在圖9-11中。

JOptionPane optionPane = new JOptionPane();
optionPane.setMessage("I got an icon and a text label");
optionPane.setMessageType(JOptionPane.INFORMATION_MESSAGE);
Icon icon = new DiamondIcon (Color.BLUE);
JButton jButton = OptionPaneUtils.getButton(optionPane, "OK", icon);
optionPane.setOptions(new Object[] {jButton} );
JDialog dialog = optionPane.createDialog(source, "Icon/Text Button");
dialog.setVisible(true);

Swing_9_11

監聽屬性變化

JOptionPane類定義了下列11個常量來輔助監聽邊界屬性的變化:

  • ICON_PROPERTY
  • INITIAL_SELECTION_VALUE_PROPERTY
  • INITIAL_VALUE_PROPERTY
  • INPUT_VALUE_PROPERTY
  • MESSAGE_PROPERTY
  • MESSAGE_TYPE_PROPERTY
  • OPTION_TYPE_PROPERTY
  • OPTIONS_PROPERTY

SELECTION_VALUES_PROPERTY

  • VALUE_PROPERTY
  • WANTS_INTUT_PROPERTY

如果我們沒有使用JOptionPane的工廠方法,我們可以使用PropertyChangeListener來監聽邊界屬性的變化。這可以使得我們被動的監聽邊界屬性的變化,而不是在變化後主動獲取。

9.1.5 自定義JOptionPane觀感

每一個可安裝的Swing觀感都提供了不同的JOptionPane外觀以及默認的UIResource值集合。圖9-12顯示了預安裝的觀感類型Motif,Windows,以及Ocean的JOptionPane窗口的外觀。

Swing_9_12

JOptionPane的消息類型幫助確定要在選項面板的圖標區域顯示的默認圖標。對於普通的消息,並沒圖標。其餘的四個圖標-用於信息,問題,警告以及錯誤消息-顯示在表9-4中。

Swing_table_9_6

表9-5中顯示了JOptionPane可用的UIResource相關屬性的集合。對於JOptionPane組件,有56種不同的屬性。

Swing_table_9_7_1Swing_table_9_7_2Swing_table_9_7_3Swing_table_9_7_4

表9-5中所列資源的一個很好的用法就是用來自定義默認的按鈕標籤從而來匹配用戶的locale或是語言。例如,要將Cancel,No,OK以及Yes按鈕的標籤修改爲法語,可以在我們的程序中添加下面的代碼。(我們也可以由java.util.ResourceBundle中獲取翻譯的文本。)

// Set JOptionPane button labels to French
UIManager.put("OptionPane.cancelButtonText", "Annuler");
UIManager.put("OptionPane.noButtonText", "Non");
UIManager.put("OptionPane.okButtonText", "D'accord");
UIManager.put("OptionPane.yesButtonText", "Oui");

現在當我們顯示在選項面板時,按鈕將會具有本地化的按鈕標籤。當然,這需要爲選項面板翻譯消息。圖9-13顯示在下面的代碼所創建的彈出窗口的樣子。因爲彈出窗口的標題並不是一個屬性,我們必須將標題傳遞給每一個所創建的對話框。

int result = JOptionPane.showConfirmDialog(
  aFrame, "Est-ce que vous avez 18 ans ou plus?", "Choisisez une option",
  JOptionPane.YES_NO_CANCEL_OPTION);

Swing_9_13

JOptionPane組件支持本地化的JOptionPane按鈕標籤。JOptionPane可以爲標準的Yes,No,Cancel與OK按鈕顯示相慶的中文或是日文按鈕標籤。例如,圖9-14中左側顯示了帶有日文標籤的Yes,No與Cancel按鈕標籤,而右側則顯示了帶有日文標籤的OK與Cancel按鈕標籤。很明顯,我們需要修改選項面板中的消息。

Swing_9_14

幸運的是,JDK 5.0版本包含了對於標籤JOptionPane(同時還有JFileChooser與JColorChooser)標籤的翻譯。這可以用於德語(de),西班牙語(es),法語(fr),意大利語(it),日語(ja),韓語(ko),英語,瑞典語(sv),以及中文(簡體/zh_CN與繁體/zh_TW)。