Presentation is loading. Please wait.

Presentation is loading. Please wait.

第 18 章 圖形使用者介面.

Similar presentations


Presentation on theme: "第 18 章 圖形使用者介面."— Presentation transcript:

1 第 18 章 圖形使用者介面

2 本章提要 18-1 甚麼是圖形使用者介面? 18-2 Java 的 GUI 基本架構 18-3 GUI 的事件處理 18-4 版面配置管理員
18-5 2D 繪圖 18-6 綜合演練

3 18-1 甚麼是圖形使用者介面? 圖形使用者介面 (Graphics User Interface, 簡稱 GUI) 意指以圖像的方式與使用者互動:程式顯示的訊息、資訊都是以圖形的方式表現、也提供圖形化的操作介面, 讓使用者能據以操作程式。最極端的例子就是電視遊樂器或電腦遊戲, 這些遊戲提供的使用者界面就是完全圖形化的;

4 甚麼是圖形使用者介面? 而個人電腦上常見的 Windows 作業系統也是以圖形使用者介面為主, 其下的應用程式也多是使用圖形使用者介面, Windows 應用程式一般都是以視窗 (Window) 的方式呈現, 而它可能會有功能表、按鈕、圖示...等各式各樣的元件在上面, 供使用者操作使用。

5 甚麼是圖形使用者介面?

6 甚麼是圖形使用者介面? 本書之前所寫的範例程式, 都未提供圖形使用者介面, 它們都只是文字模式 (Text-based) 的應用程式, 程式顯示的訊息、使用者的輸入都只是文字而已。就算程式用一些符號畫出一張圖片, 那仍是以字元畫出來的;而且也無法像視窗程式從滑鼠、按鈕、功能表等圖形元件取得使用者輸入。

7 甚麼是圖形使用者介面? 如果要讓 Java 程式也能在微軟 Windows 作業系統或 Linux/Unix 的 X Window 環境下以圖形的方式呈現, 就必須使用 Java 的 AWT 或 Swing 套件, 以它們提供的各種 GUI 元件來『畫出』程式的使用者介面。 本章要介紹的 GUI 元件以 Swing 套件為主, 但我們也需要用到 AWT 套件中的一些功能, 以下就先來認識這兩個套件。

8 18-2 Java 的 GUI 基本架構 Java AWT 與 Java Swing 認識 javax.swing 套件

9 Java AWT 與 Java Swing 早在 Java 剛推出之時, Java 提供的只有 AWT (Abstract Window Toolkit) 這套 GUI 元件 (或稱 GUI 類別庫), 但在 Java 1.1 時又推出了 Swing 這一組強化的GUI 元件類別庫, 以下簡單介紹兩者的功能與差異。

10 功能簡易的 AWT AWT 這個抽像化的視窗工具組 (Abstract Window Toolkit) 提供了一套最基本的 GUI 元件, 例如視窗、按鈕等等, 在程式中只要建立所需的 GUI 元件類別物件, 就能在螢幕上畫出一個元件。 AWT 元件在繪製時, 其實都是由對應的對等類別 (peer class) 以各作業系統提供的方式, 畫出現成的原生 (native) 元件。

11 功能簡易的 AWT

12 功能簡易的 AWT 例如我們的 Java 程式用 AWT 元件在 Windows 環境下顯示個按鈕, 此時AWT 會呼叫 Windows 作業系統本身的 GUI 功能來顯示一個按鈕;如果把同一個程式拿到 X Window 環境下執行, 就是由 X Window 來畫出這個按鈕。

13 功能簡易的 AWT 由於不同的圖形使用者介面, 其表現方式各異, 所以依賴對等類別來繪製元件的 AWT, 在使用上就需面對一個問題:程式顯示的 GUI 介面, 在不同的作業系統上可能長得不一樣。

14 功能簡易的 AWT 除了提供 GUI 元件這些看得到的物件外, AWT 也提供了設計視窗類型的應用程式所需的一些功能與架構, 例如事件驅動 (Event-driven) 的程式設計模式, 在本章稍後就會介紹。

15 進化的 Swing 如前述, Java 在 1.1 版時加入了 Swing 這一組新的 GUI 功能, 但當時 Swing 仍不算是 Java 的核心 (core), 也就是說它仍只是一個外加的擴充模組, 但到了1.2 版時, Swing 就被納入為 Java 核心的一部份。

16 進化的 Swing Swing 和 AWT 最大的不同, 就是它並不依賴作業系統的對等類別來繪製各種 GUI 元件, 所有的 Swing 元件都是由 Java 自行繪製。由於不依賴作業系統來繪製 GUI 元件, 所以使用 Swing 繪製出來的 GUI 介面, 就能保有一致的外觀和行為。

17 進化的 Swing 不過 Swing 並未取代掉 AWT, 因為 Swing 元件的實作雖然是由 Java 自己來繪製, 但 Swing 仍算是架構於 AWT 之上, 例如在 GUI 環境下所需的事件驅動模型, 就仍是沿用 AWT 所提供的架構。

18 進化的 Swing 雖然 AWT 和 Swing 各有其優缺點, 但基於 Swing 提供更全面的 GUI 設計功能, 因此本章介紹的 GUI 設計將以 Swing 元件為主。不過讀者也不需擔心不會 AWT, 因為設計 GUI 時所需的事件驅動程式設計架構、元件配置管理員都仍是源自於 AWT, 所以我們也會介紹這一部份的 AWT 功能, 因此不管您日後需使用 AWT 或 Swing, 都能輕鬆駕御自如。

19 認識 javax.swing 套件 要用 Swing 來設計程式的 GUI 也離不開這種模式:
匯入 Swing 套件:Swing 套件和我們前面用過的套件有點小小的不同, 其套件名稱是javax.swing, 請注意多了一個 x。 使用 Swing 類別建立物件:匯入 Swing 套件後, 我們就可利用它所提供的各種 GUI 元件類別, 建立 GUI 物件了。

20 Component 類別 javax.swing 套件中最常用到的一群類別, 就是各種 GUI 元件 (Component) 類別了, 舉凡按鈕、功能表、工具列、文字方塊、下拉式選單等, 都有其對應的類別可使用, 以下就是 javax.swing 套件中一些常用的元件類別:

21 Component 類別

22 Component 類別 由上圖可發現 Swing 是建立在 AWT 之上的事實, 因為 JComponen 這個上層的 Swing 元件類別, 是繼承自 AWT 的 Container 類別。另外我們也可發現Swing 類別的一項共通點, 就是類別名稱大半以大寫的 J 開頭, 後面則接上元件或其它名稱。 例如我們想顯示一段文字訊息, 就可建立 JLabel 物件;要顯示個簡單的確定鈕, 就可建立一個 JButton 物件。

23 容器類別 大家可以回想一下, 可曾在 Windows 環境中看過一個浮在畫面上、不屬於任何視窗的訊息或按鈕?沒錯, 要顯示各種 GUI 元件, 必須先有視窗或其它類型的容器物件 (Container) 來包含這個元件, 所以程式必須先建立一個容器物件, 才能將所需的元件加到容器物件之中, 並顯示出來。

24 容器類別 容器 (Container) 也算是一種元件, 一如其名, 它是用來容納其它元件用的。延續剛剛的例子:如果想顯示一段文字訊息, 並有一個確定鈕讓使用者按, 就必須建立一個容器物件來容納文字訊息和按鈕, Swing 的容器物件有 JFrame、JDialog、JInterFrame、JPanel、JWindow 等五種。

25 容器類別 JFrame:典型的視窗, 要建立一般的視窗都是使用 JFrame。 JDialog:交談窗類型的視窗。
JWindow:不含視窗標題等基本視窗要件的陽春型視窗。 JInterFrame、JPanel:這兩者並不能用來建立獨立的視窗, 它們必須是包含在前三種容器之中使用。例如 JInterFrame 是用來建立視窗內的子視窗;而 JPanel 可建立視窗中的面板。

26 容器類別 這幾個容器類別的繼承關係如下圖:

27 容器類別 JFrame、JDialog、JWindow 都是 java.awt.Window 的衍生類別, 後者定義了一組視窗容器共用的操作方法, 以下就是幾個基本的方法:

28 容器類別

29 陽春的視窗程式 認識視窗的類別後, 我們就來寫個陽春的視窗程式, 此範例只是單純顯示視窗, 並無任何功能:

30 陽春的視窗程式

31 陽春的視窗程式

32 陽春的視窗程式 第 1 行, 要使用 Swing 物件, 程式開頭一定要匯入 javax.swing 套件。
第 7 行建立一個 JFrame 元件, 並在呼叫建構方法時以一個字串為參數, 此字串將會成為視窗標題欄的內容, 如執行結果上的圖片所示。

33 陽春的視窗程式 第 10 行呼叫 setDefaultCloseOperation() 方法, 以設定當使用者關閉視窗時要做什麼動作。EXIT_ON_CLOSE 這個常數的意思, 就是指在關閉視窗時即結束程式, 這也是一般視窗應用程式的行為。 第 12、13 行呼叫前面介紹過的 setSize()、setVisible() 方法設定視窗的大小, 並顯示視窗。

34 在視窗中加入元件 認識如何建立視窗後, 接下來就要看如何將元件加到視窗之中。有趣的是, 我們不能直接將元件加到 JFrame、JDialog 這些上層的容器, 而是必須加到這類容器的 Content Pane 容器。 JFrame、JDialog、JWindow 都有個稱為 Content Pane 的子容器, 要在 JFrame 視窗、JDialog 交談窗中加入的 GUI 元件, 都必須加到這個稱為 Content Pane 的子容器中。

35 在視窗中加入元件 我們可以把 JFrame 想成是視窗的外框, 這個外框除了有基本的視窗邊框、標題欄、以及右上角的縮放視窗和關閉按鈕, Content Pane 則是代表視窗中實際可用的區域, 也就是我們可加入各種元件的地方:

36 在視窗中加入元件

37 在視窗中加入元件 要取得 JFrame 的 Content Pane, 可呼叫以下方法:
接著即可呼叫以下繼承自 java.awt.Container 的方法, :

38 在視窗中加入元件 知道要用容器來容納元件後, 我們就能寫一個在視窗中加入一個 JButton 按鈕物件的範例程式。此程式會先建立 JFrame、JButton 物件, 然後用 JFrame 物件取得 Content Pane 以加入 JButton 元件, 之後就和前一個範例一樣, 用 setVisible() 方法將視窗顯示出來:

39 在視窗中加入元件

40 在視窗中加入元件

41 在視窗中加入元件

42 在視窗中加入元件 讀者可從執行結果發現兩件事:這個按鈕沒有任何功能, 按下去都沒什麼反應;其次是這個按鈕會佔用 Content Pane 的全部空間, 當我們調整視窗大小時, 這個按鈕也會隨視窗一起變大或變小, 和我們一般認知的固定大小按鈕不同。 要解決這兩個問題, 必須先認識 AWT/Swing 的事件處理模型與版面配置架構。

43 JDK 5.0 預設的圖形介面樣式 使用 JDK 5.0 版編譯執行程式的讀者, 或許會發現上述範例程式的畫面是有漸層的色彩樣式, 和書中的圖片略有不同。這是因為 JDK 5.0 系統預設的圖形介面樣式 (Look and Feel) 從原本的 Metal 樣式 (書中圖片所採用者) 換成 Ocean 樣式。 若想使用和舊版相同的 Look and Feel, 您可在程式中 (例如 main() 方法最前面) 加入如下的程式:

44 18-3 GUI 的事件處理 委派式事件處理架構 實作 Listener 介面 內部類別 以匿名類別設計事件處理類別與方法
繼承 Adapter 類別處理事件

45 委派式事件處理架構 在 AWT 的委派式件處理模型中, 每當使用者在 GUI 中做一項動作時, 例如按下按鈕、選擇某個選項、移動一下滑鼠, 都會產生對應的事件 (event)。

46 委派式事件處理架構 如果我們要讓程式會在使用者按下按鈕時執行一項動作, 就必須撰寫一個事件處理方法來處理對應的事件 (例如按鈕被按下的事件)。寫好事件處理方法後, 還必須通知會產生該事件的元件:我的物件要當事件的傾聽者(Listener), 如此在發生事件時, 元件才會呼叫對應的事件處理方法。

47 委派式事件處理架構

48 委派式事件處理架構 實作 XXXListener 介面:首先, 包含事件處理方法的類別必須宣告為實作 XXXListener 介面。不同類型的事件需實作不同的介面, 下表所列即為幾種常見的 Listener 介面:

49 委派式事件處理架構 撰寫事件處理方法:事件處理方法就是在上述的 Listener 介面中所宣告的方法, 每種介面所宣告的方法數量都不一。例如 ActionListener 只有一個 actionPerformed() 方法。MouseMotionListener 介面則有 mouseDragged()、mouseMoved() 兩個方法。

50 委派式事件處理架構 而我們需在方法中將回應事件的動作寫出, 例如您希望使用者按某個按鈕時改變視窗的背景顏色, 就要將改變視窗背景顏色的程式碼加到 action Performed() 方法中。

51 委派式事件處理架構 告知元件我們要當傾聽者:我們必須在程式中呼叫按鈕元件的addActionListener() 方法通知該元件, 我們的物件是個傾聽者, 也就是說物件中有對應的事件處理方法。如此一來, 發生按鈕被按下的事件時, 按鈕元件才會呼叫我們寫好的 actionPerformed() 方法, 執行我們希望執行的動作。

52 委派式事件處理架構 呼叫 addActionListener() 方法時, 需以實作 ActionListener 介面的物件為參數, 也就是告知按鈕元件:我這個物件是個傾聽者。 請注意, 我們並不會在自己的程式中呼叫 actionPerformed() 事件處理方法, 這個方法是提供給元件呼叫的。所以事件處理架構, 算是一種被動的處理方式。

53 委派式事件處理架構 如果在程式執行生命期中, 使用者都沒有按下按鈕, 則對應的 actionPerformed() 方法也不會被執行到。

54 實作 Listener 介面 認識 AWT 的事件處理模型後, 我們就來看如何寫一個會處理按鈕事件的傾聽者。由於此部份的功能是源自於 AWT, 所以程式開頭必須匯入 awt.event.* 套件。 要實作 Listener 介面, 您可以在主程式外另建一個類別, 然後將它宣告為實作 Listener 介面、並實作事件處理方法。

55 實作 Listener 介面 不過這樣做並不方便, 因為我們通常會在事件處理方法中使用到視窗容器或其它元件物件, 此時要取得該物件將會使程式變得複雜, 所以我們仍是用主程式本身來實作 Listener 介面。 以處理按鈕事件為例, 根據前面所述, 我們必須完成 3 個動作:

56 實作 Listener 介面 宣告實作 Listener 介面:這部份大家都已很熟悉, 以按鈕事件為例, 在類別宣告後加上如下程式即可:

57 實作 Listener 介面 撰寫事件處理方法:由於 ActionListener 只宣告一種 actionPerformed() 方法。所以我們只要實作這個方法, 即可處理按鈕事件:

58 實作 Listener 介面 告知元件我們要當傾聽者:我們必須以按鈕物件呼叫 addActionListener() 方法:
以下就是將前一個範例程式加上按鈕事件處理功能的版本:

59 實作 Listener 介面

60 實作 Listener 介面

61 實作 Listener 介面

62 實作 Listener 介面

63 實作 Listener 介面 第 6 行宣告的變數 act 是用來記錄按鈕被按下的次數。
第 8〜10 行的 main() 方法只有建立類別物件的敘述。

64 實作 Listener 介面 第 13〜24 行的 建構方法進行主要的 GUI 建立動作, 除了我們已熟悉的建立元件、將元件加入視窗、顯示視窗外, 最重要的就是第 18 行以 this 物件為參數, 呼叫 mybutton.addActionListener() 方法, 表示將本物件設為 mybutton 按鈕的動件事件之傾聽者。

65 實作 Listener 介面 第 26〜31 行即為事件處理方法 actionPerformed() 的內容, 方法中先將 act 變數加 1, 然後用 myframe 物件呼叫 setTitle() 方法, 將視窗標題變更為顯示按鈕的次數。所以每按一次按鈕, 這個方法就會被呼叫一次, 視窗標題顯示的數字也會隨之遞增。

66 實作 Listener 介面 由上述程式可發現一件有趣的事:在我們的程式中根本沒有呼叫 actionPerformed() 的敘述, 但從執行結果可確認, 每次按按鈕時, 此方法就會被呼叫一次。由於事件處理方法是被元件 (或系統) 呼叫, 不像平常是由我們呼叫 (Call) 元件 (或系統) 提供的方法, 所以事件處理方法也稱為 Call-Back 方法。

67 如何處理多個按鈕的事件? 如上所示, 加入事件處理方法的步驟並不難, 大家可能也躍躍欲試想在視窗多加幾個元件或多加幾個按鈕, 但這時您會發現一件事:假如視窗中有兩個按鈕, 這兩個按鈕被按下時要做不同的事, 但類別中只能有一個 actionPerformed() 方法, 該怎麼辦呢?

68 如何處理多個按鈕的事件? 有個方法是在 actionPerformed() 方法中, 用 if、switch 等方式先檢查產生此事件的元件是誰, 再決定要做什麼樣的動作:

69 如何處理多個按鈕的事件? 但此法只適用於性質/功能近似的方法 ( 節的範例程式 ChangeColor.java 即是採此種作法), 如果用來處理完全不同性質的按鈕元件, 則這種寫法不但不夠物件導向, 當同類型的元件很多時, 事件處理方法的內容將會變得很大, 造成撰寫及閱讀上的不方便。因此這時我們就需要 Java 語言的另一項功能:內部類別和匿名類別。

70 內部類別 根據 Java 語言規格, 定義在另一個類別內部的類別就稱為巢狀類別 (Nested Class), 而內部類別 (Inner Class) 則是未被明確或隱含宣告為static 的巢狀類別。簡單的說, 內部類別就是:定義在另一個類別內部, 且非 static 的類別。

71 內部類別 相對於內部類別而言, 包含住它的類別則稱為外部 (Outter) 類別, 或稱外層類別。

72 內部類別 我們學過, 類別內部的方法可存取類別內容的成員, 內部類別由於是定義在別的類別之內, 所以它的最大特點, 就是可存取外部類別的成員 (但宣告為 static 的巢狀類別則無法存取外部類別的成員)。

73 以內部類別設計傾聽者 內部類別為非靜態的巢狀類別, 其特點是可直接存取外部類別的私有成員, 而無任何限制。如果我們要用內部類別的方式來設計事件的傾聽者, 只需將這個內別類別宣告為實作指定的介面, 再撰寫事件處理方法即可。例如前面的按鈕範例, 以內部類別設計改寫如下:

74 以內部類別設計傾聽者

75 以內部類別設計傾聽者

76 以內部類別設計傾聽者

77 以內部類別設計傾聽者 這個範例的執行情形和前面的 SimpleListener.java 完全相同, 但此處改用內部類別的方式來設計傾聽者, 傾聽者類別 InnerListener 的定義在第 26〜31 行, 程式中則是在第 16 行建立傾聽者類別物件, 並將它設定為按鈕事件的傾聽者。

78 匿名類別 區域內部類別中還有一種特別的形式, 稱為匿名類別 (Anynomous Class), 也就是說它只有類別的本體, 但沒有類別的名稱, 更進一步的說, 連物件的參照變數都不需宣告。或者我們也可說匿名類別是:在使用物件時, 才同時定義類別和產生物件的類別。 物件導向程式設計的特性之一, 就是軟體元件 (類別) 的重複使用性。

79 匿名類別 在一般情況下, 我們先定義好一個類別, 即可在程式中用它建立多個物件、必要時則可由既有類別來衍生新類別。
但有時候我們所設計的類別, 在程式中只會用到一次, 根本不會重複使用。費時去定義一個類別, 再來建立物件, 就顯得沒有必要了, 此時就能利用匿名類別的技巧來建立此類別物件, 同時也有使程式碼較簡化的功效。

80 匿名類別 建立匿名類別的方式很簡單, 像前面所說的, 只要在需要用到匿名類別物件的敘述上, 直接將類別定義放在原本在物件名稱的地方 (同時也建立了類別物件), 就可以建立一個匿名類別物件了。請注意, 匿名類別必須衍生自任一既有類別或介面, 所以建立匿名類別物件的語法如下:

81 匿名類別 以上的程式片段就會建立一個匿名類別物件, 而且可馬上使用, 例如當成一個方法的參數, 或是直接呼叫匿名類別本身的方法。我們直接看一個簡單的範例, 就能明白其用法:

82 匿名類別

83 匿名類別 程式第 6〜13 行的 . 之前的程式碼就是在建立一個匿名類別物件, 此匿名類別衍生自 Object, 其中並定義了一個整數成員 b 及公開的方法 show(), 接著在第 13 行就直接以 .show() 的方式呼叫此匿名類別的 show() 方法, 所以程式會輸出 show() 方法所顯示的訊息。

84 以匿名類別設計事件處理類別與方法 當視窗有多個按鈕, 各按鈕要有不同的事件處理方法, 但又不適合用一個類別來實作所有按鈕的傾聽者, 此時就可利用內部類別或匿名類別來實作不同按鈕元件的傾聽者, 這樣就做到讓每個按鈕都有自己的傾聽者, 因此也能讓每個按鈕能有不同的行為。 以下就是將前面的視窗程式 SimpleListener.java, 改寫成用匿名類別來實作傾聽者的範例:

85 以匿名類別設計事件處理類別與方法

86 以匿名類別設計事件處理類別與方法

87 以匿名類別設計事件處理類別與方法

88 以匿名類別設計事件處理類別與方法 此範例程式和前面的 SimpleListener.java 差不多, 最大的不同就在第 18〜26 行呼叫按鈕元件的 addActionListener() 方法, 呼叫此方法所給的參數就是第 20 〜25 行所建立的匿名類別物件。當按鈕物件被按下產生事件時, 即會執行到第 21 〜24 行的處理方法, 也就是在視窗標題欄顯示按鈕的次數。

89 以匿名類別設計事件處理類別與方法 認識內部類別、匿名類別、解決多個同類 GUI 元件的事件處理後, 稍後在 18-4 節就會介紹如何利用 AWT 的版面配置管理員來控制、排列多個 GUI 元件。

90 繼承 Adapter 類別處理事件 我們都知道, 類別要實作介面時, 需實作介面中所定義的所有方法。
在實作各種事件處理介面時也不能例外, 例如有關鍵盤按鍵事件的 KeyListener 介面就有: keyPressed() keyReleased() keyTyped() 三個方法

91 繼承 Adapter 類別處理事件 而滑鼠事件的 MouseListener 介面則更多, 共有: mousePressed()
mouseReleased() mouseEntered() mouseExited() mouseClicked()。

92 繼承 Adapter 類別處理事件 有時我們只想處理其中 1、2 種方法, 卻也必須實作所有的方法, 雖然只需讓沒有用到的方法的本體保持空白即可, 例如:

93 繼承 Adapter 類別處理事件 但仍要在程式中列出一些未用到的方法, 總是有點不方便。為此 Java 提供了另一套機制, 也就是使用 Adapter 類別。 Adapter 類別是一組實作各種事件處理方法的抽象類別, 當我們想要處理某類事件中的 1、2 種事件時, 可讓我們的類別繼承對應的 Adapter 類別, 然後只重新定義您要用的事件處理方法即可, 如此就不必在程式中列出一堆未用到的方法。

94 繼承 Adapter 類別處理事件 java.awt.event 中定義了 7 個 Adapter 類別, javax.swing 則增加了2 個, 如下表所示:

95 繼承 Adapter 類別處理事件 要用 Adapter 類別處理事件, 就如前面所述, 只需繼承對應的 Adapter 類別, 然後重新定義所需的事件處理方法即可。

96 繼承 Adapter 類別處理事件 舉例來說, 若只想處理按鍵事件中的 keyTyped() 方法, 可繼承 KeyAdapter 類別後, 改寫 keyTyped() 方法為我們所要的功能即可, 當然也要呼叫 addKeyListener() 方法將類別物件設為傾聽者, 例如以下的範例:

97 繼承 Adapter 類別處理事件

98 繼承 Adapter 類別處理事件

99 繼承 Adapter 類別處理事件

100 繼承 Adapter 類別處理事件

101 繼承 Adapter 類別處理事件 第 04 行宣告我們的類別繼承自 KeyAdapter 抽象類別。
第 18 行呼叫 addKeyListener() 方法將 this 物件設為傾聽者。 第 27〜29 行為自訂的 keyTyped() 方法, 以處理由按鍵輸入字元的事件。

102 繼承 Adapter 類別處理事件 第 28 先用事件物件 e 呼叫 getKeyChar() 方法取得使用者輸入的字元, 再用 GUI 元件的 setText() 方法將指定的字串及輸入的字元設為標籤所顯示的文字。 其它 Adapter 類別的用法也都相似, 此處就不一一介紹。讀者可參考各 Adapter 類別的說明文件, 瞭解各種事件的效用, 即可利用 Adapter 類別撰寫出不同事件的處理方法。

103 keyPressed 與 keyTyped 的差異
keyPressed 是按鍵盤上的按鍵被按下時產生的事件;至於 keyTyped 則是有某個字元被輸入時所產生的事件。例如當我們只按下 [Shift] 鍵時, 雖然產生 keyPressed 事件, 但因單純的 [Shift] 鍵不代表任何字元, 所以不會產生 keyTyped 事件;若再按下 [A] 鍵才會產生大寫 (或小寫) 的字元 'A'。

104 keyPressed 與 keyTyped 的差異

105 18-4 版面配置管理員 如果您想在前述的範例程式中多加幾個按鈕, 例如再加個按鈕讓程式顯示的按鈕次數遞減, 您會發現程式仍只顯示一個按鈕。
這是因為 JFrame 有預設的版面配置 (layout) 方式, 如果不依其規則指定按鈕的位置, 則按鈕都會放到同一個地方, 使得視窗上只看得到一個按鈕;除了指定按鈕位置外, 您也可改變預設的配置方式。

106 版面配置管理員 不管使用哪一種方法, 我們都需用到 AWT 的版面配置管理員 (layout manager), 以下就來認識 Java 的 GUI 元件配置方式。

107 GUI 元件配置的基本觀念 在 Java 的 GUI 介面設計中, 是透過 AWT 的版面配置管理員來控制 GUI 元件在容器中的排列方式和位置。A WT 中定義的配置管理員多達十餘種, 每種都有其特殊的排列方式和規則, 以供不同需求的視窗程式選用。

108 GUI 元件配置的基本觀念 使用版面配置管理員的好處, 在於程式設計人員不需擔心 GUI 元件在視窗中的位置, 只要選擇合適的版面配置管理員, 不論使用者如何調整視窗的大小, 版面配置管理員都會依其內建的排列規則, 將 GUI 元件陳列在視窗之中。

109 GUI 元件配置的基本觀念 AWT 中定義的配置管理員雖然有很多種, 但一般我們比較會用到的不外以下幾種 (每個配置管理員都有一個對應的同名類別): BorderLayout FlowLayout GridLayout

110 GUI 元件配置的基本觀念 如果要做較複雜的元件排列和配置, 其實使用各種 Java 整合開發環境 (IDE, 例如本書所附的 JBuilder) 提供的工具來繪製會比較方便, 在此就不多做介紹。 Swing 中的每個容器類別都有其預設的版面配置管理員, 以下我們就先從 JFrame 預設使用的 BorderLayout 談起。

111 BorderLayout 配置管理員 JFrame 預設採用的 BorderLayout 配置管理員, 是比較固定的的一種配置方式。BorderLayout 將可用的空間劃分為 5 個部份, 如下所示:

112 BorderLayout 配置管理員 圖中的 NORTH 、WEST 等就是在加入元件時, 需指定的位置參數 (稱為 Constraint), 表示要將元件加到哪一個位置上:

113 BorderLayout 配置管理員 BorderLayout 除了位置固定外, 還有幾個特點:加入某位置的元件將會填滿整個位置;放大視窗時, 將以放大中間的部份為主, 上、下位置則是依視窗放大的情況動態調整。 以下就是一個應用 BorderLayout 的簡例, 這個程式會用到 JLabel、JText Field 這 2 個新的 GUI 元件。

114 BorderLayout 配置管理員

115 BorderLayout 配置管理員

116 BorderLayout 配置管理員

117 BorderLayout 配置管理員

118 BorderLayout 配置管理員

119 BorderLayout 配置管理員

120 BorderLayout 配置管理員 第 7〜11 行建立 JFrame、及視窗中的按鈕和文字輸入欄位等物件。
第 22〜26 行呼叫 add() 方法將 5 個元件加到 BorderLayout 的五個位置。第 22 行加入的 JLable 物件因為只是用來顯示文字, 在程式中不會再用到, 因此是在 add() 方法內直接用 new 敘述立即建立。 第 29 〜43 行是用匿名類別建立華氏轉攝氏按鈕的處理方法。

121 BorderLayout 配置管理員 第 34 行用 getText() 取得使用者輸入的字串, 並將之轉成浮點數。程式並用 try/catch 的方式補捉轉換時可能發生的格式不符合的例外。 第 38〜40 行的 catch 區塊會在使用者輸入非數字字串並按按鈕時, 將輸入區清除為空白。 第 48〜58 行是攝氏轉華氏按鈕的處理方法, 除了計算公式不同, 其內容與華氏轉攝氏的處理方法相同。

122 BorderLayout 配置管理員 BorderLayout 的特點就是配置的方式/位置都預先設好了, 無法做變動。但如果要放的元件不到 5 個, 則仍是有些彈性, 因為未用到的區域, 在視窗上就不會顯示出來, 所以就能產生不同的配置效果, 例如:

123 BorderLayout 配置管理員 例如下圖就是將 TempConverter.java 的第 20、21 行的程式去掉時, 所產生的視窗畫面:

124 BorderLayout 配置管理員 BorderLayout 的主要缺點就是只有五個區域, 最多只能放五個元件。若要放置更多的元件, 就需放入額外的容器物件;若想讓元件以不同的方式排列, 則需改用其它的配置管理員。基於這兩個理由, 一般視窗程式都會換用其它的配置管理員, 以下就來介紹常用的 FlowLayout 配置管理員。

125 FlowLayout 配置管理員 FlowLayout 配置管理員排列元件的方式很簡單, 就是將加入的物件依序由容器左上角向右排列, 到視窗右邊界時會自動換行繼續排列, 所以各元件間的相對位置可能會隨視窗大小不同而變動。

126 FlowLayout 配置管理員 Swing 中預設採用 FlowLayout 配置管理員的容器類別是 JPanel, 一般在設計 GUI 程式時, 並不會將元件直接加到 JFrame, 而是將 JPanel 加到 JFrame 中, 然後再將所需的元件加入 JPanel 容器, 如此會比較有彈性。但如果您要設計的 GUI 並不複雜, 而想直接在 JFrame 使用 FlowLayout 配置管理員, 則可用 Content Pane 物件的 setLayout() 方法來設定:

127 FlowLayout 配置管理員 以下就是使用 JPanel 的簡單範例, 此範例將 5 個按鈕元件加到 JPanel 容器中, 因此元件的排列方式是由 FlowLayout 配置管理員處理。讀者可從此認識 FlowLayout 配置的特性:

128 FlowLayout 配置管理員

129 FlowLayout 配置管理員

130 FlowLayout 配置管理員

131 FlowLayout 配置管理員

132 FlowLayout 配置管理員

133 FlowLayout 配置管理員 第 7〜11 行建立 5 個可改變視窗背景顏色的按鈕。
第 18 行建立 JFrame 物件, 並在第 19 行將 ChangeColor (JPanel) 物件加到 JFrame 中。 第 25〜39 行為 ChangeColor 的建構方法, 第 27〜31 行用 JPanel 的 add() 方法將 5 個按鈕元件都加到其中;第 34〜38 行將 5 個按鈕的傾聽者都設為 this 物件。

134 FlowLayout 配置管理員 第 42〜51 行為 5 個按鈕共用的事件處理方法, 發生按鈕事件時, 程式先在第 43 行以事件物件呼叫 getSource() 方法取得產生事件的按鈕物件, 接著用 if/else 敘述比對按鈕物件, 然後用 JPanel 物件呼叫 setBackground() 方法設定 JPanel 的背景顏色。

135 FlowLayout 配置管理員 調整視窗大小時, 就能察覺 FlowLayout 的動態配置效果:當視窗夠寬時, 按鈕都會放在同一列;當視窗變窄時, 按鈕就會被擠到下一行顯示。

136 GridLayout 配置管理員 GridLayout 配置管理員和 BorderLayout 有個相似之處, 就是它們都提供制式的元件配置方式。但 GridLayout 配置管理員的制式排列並不是由 Java 預先定死的, 而是由我們自行指定將視窗切割成幾個部份, 接著就能將 GUI 元件擺到指定的位置上。

137 GridLayout 配置管理員 GridLayout 配置管理員是將容器內部切割成整齊的格子 (就像試算表的格子一樣), 放置元件時, 就是放到每個固定住的格子上面。但格子的多寡則是由我們自行指定, 在建立 GridLayout 配置管理員物件時, 需在建構方法中指定格子的行數與列數。

138 GridLayout 配置管理員 以下我們沿續前一個改變視窗背景顏色的範例, 將程式改成在 JFrame 中放入使用 GridLayout 配置管理員的 JPanel 容器, 然後再將數個元件放入 JPanel 中依序排列:

139 GridLayout 配置管理員

140 GridLayout 配置管理員

141 GridLayout 配置管理員

142 GridLayout 配置管理員

143 GridLayout 配置管理員

144 GridLayout 配置管理員 第 8、9 行分別是 JRadioButton (單選鈕) 物件的名稱字串陣列及單選鈕物件陣列。
第 17、18 行取得 JFrame 的 Content Pane, 並將用來顯示顏色的 JPanel 物件加入其中。 第 20 〜22 行建立用來放置單選鈕的 JPanel 物件, 並將之設為使用GridLayout 配置管理員 (2 列 2 行)。

145 GridLayout 配置管理員 第 23 行建立 Swing 的 ButtonGroup 類別物件, 此類別的功用是可將多個單選鈕設為一組。設為同一組的單選鈕, 每次都只能選擇一個, 使用者選擇任一單選鈕時, 同組中其它的單選鈕都會自動取消選取。 第 26〜31 行以迴圈的方式建立各單選鈕物件, 同時將各單選鈕加入 up 面板、並設定其傾聽者 (都設為 this 物件)。

146 GridLayout 配置管理員 第 39〜49 行為各單選鈕共用的事件處理方法。第 42 行取得產生此事件的物件, 隨後比對物件, 然後將下方的面板設為對應的顏色。 試著調整視窗大小, 您會發現不管如何調整, GridLayout 配置管理員固定會將其區域分成指定的格子數 (本例為 2 × 2), 所以置入該格的元件也會隨之放大或縮小。

147 GridLayout 配置管理員 在上個範例中還用到另一類型的事件處理介面 ItemListener, 凡是可以被選取的元件, 都會實作 ItemSelectable 介面, 並可呼叫其 addItemListener() 方法來加入被選取/ 取消選取事件的傾聽者。

148 GridLayout 配置管理員 ItemListener 只有一個事件處理方法 itemStateChanged(), 在此方法中, 可透過 ItemEvent 事件物件呼叫 getStateChange() 方法取得元件是被選取 (ItemEvent.SELECTED) 或被取消選取 (ItemEvent.DESELECTED)。

149 GridLayout 配置管理員 關於 AWT/Swing 的使用原則就介紹到此, 我們已介紹了基本的觀念及 GUI 元件的應用方法, 其它更多樣化的元件用法也都大同小異, 讀者可參考 Java 文件中各元件說明, 即可在程式中應用這些元件。

150 18-5 2D 繪圖 介紹過 AWT/Swing 的應用後, 本節要接著介紹 JFC 中的 2D 繪圖 API, 利用這組 API, 我們即可在螢幕上作畫、或是以不同的字型來顯示文字, 以下先說明 Java 2D 繪圖的基本原理。

151 Java 2D 繪圖基本觀念 Java 2D API 的類別分散於 java.awt、java.awt.geom、java.awt.font 等多組套件之中, 而其中最基本的就是 java.awt 套件中的 Graphics2D 類別。

152 Graphics2D:繪圖用畫布 要用 Java 2D API 進行繪圖, 就必須認識 Graphics2D 類別。在最初的 Java 版本中, 有關繪圖的部份是使用 AWT 中的 Graphics 類別。但就像是 Swing 架構於 AWT 之上並提供了更多、功能更強的 GUI 元件一樣, 在 Java 1.2 時推出的 Graphics2D, 則是 Graphics 的衍生類別, 並提供更多的 2D 繪圖功能。

153 Graphics2D:繪圖用畫布 Graphics2D 類別就像我們在作畫時用的畫布及繪圖工具, 我們可透過它所提供的方法來設定各種繪圖屬性, 例如定義畫布的顏色 (背景顏色)、畫筆的樣式、字型種類、圖形內部的塗滿顏色等等, 當然也有繪製圖形、顯示影像的方法可使用。要在螢幕上顯示圖形, 一定要先取得一個 Graphics2D (或Graphics) 物件, 不過這個物件並非由我們自行產生, 而是由系統提供。

154 Graphics2D:繪圖用畫布 回想一下 Windows 或其它 GUI 環境, 我們可在畫面上顯示多個視窗, 而每個程式可控制、畫圖的範圍, 就受限於它的視窗範圍。舉例來說, 您執行了一個繪圖程式, 並調整其視窗僅佔有螢幕右上角 1/4 的範圍, 這時候您就只能在螢幕右上角作畫, 不可能要程式在螢幕左下角也畫出線條、圖形。

155 Graphics2D:繪圖用畫布 對應到 Java , 就是說程式必須向作業系統取得一個 Graphics2D 物件, 此物件將代表程式可繪圖的範圍, 所以程式不會隨便畫在螢幕上任一角落、甚至畫到其它程式的視窗中。

156 如何取得 Graphics2D 物件 GUI 環境的特性之一, 就是視窗隨時會被其它的視窗蓋住、或是被使用者縮小不見, 而當視窗再度顯示時, 程式就需重新繪製先前被蓋住/ 看不見的地方, 以回復其原來的畫面。以 Swing 為例, 當元件或容器需要被繪製時, 系統會呼叫該元件的 Callback 方法 paintComponent() (AWT 元件則是 paint() 方法), 讓該方法重新把元件畫出來。

157 如何取得 Graphics2D 物件 前兩節使用 Swing 元件時, 我們都未改寫各元件的 paintComponent() 方法, 所以各元件都是以其原有的行為繪出。若我們想讓按鈕長得和預設的不同, 就可改寫按鈕的 paintComponent() 方法, 以自訂的方式畫出按鈕。

158 如何取得 Graphics2D 物件 paintComponent() 方法的參數即為 Graphics 物件。所以當我們想在視窗中繪圖時, 即可在 paintComponent() 中, 將其 Graphics 參數物件轉型成 Graphics2D 物件, 然後進行 2D 繪圖。

159 如何取得 Graphics2D 物件

160 Java 2D 座標系統 在進一步介紹如何利用 Graphics2D 繪製圖形之前, 我們要先認識 Java 2D 的繪圖座標系統。 Java 2D 的繪圖座標系統分為使用者空間 (User space) 及裝置空間 (Device space, 或稱硬體空間), 後者代表的是整個螢幕 (或印表機等輸出裝置) 的畫面。如前所述, 通常我們不會對整個螢幕畫面做繪圖的動作, 而是只在自己程式的容器、元件上做畫, 所以只會用到使用者空間。

161 Java 2D 座標系統 使用者空間是以元件的 最左上角為原點, 然後 以橫向為 X 軸、縱向為 Y 軸標示座標點:
以畫一條直線為例, 必需指定直線兩個端點的座標參數來呼叫畫線的方法, 下一節就來介紹如何用 Graphics2D 圖形類別來繪製圖形。

162 基本圖形的繪製 使用 Java 2D 繪圖的基本步驟如下: 取得 Graphics2D 物件。
設定畫筆樣式、顏色、粗細等。若省略此步驟, 則是以預設的畫筆樣式、顏色、粗細進行繪圖。 設定圖形的內部填色方式。 建立圖形物件, 繪製圖形。

163 基本 2D 繪圖 Java 2D 提供許多繪製基本線條、幾何圖形的類別及方法, 這些類別均實作 Shape 這個定義了一些與圖形相關的基本方法。Java 2D 的圖形類別包括 (均屬 java.awt.geom 套件): 線段:Line2D、Line2D.Double、Line2D.Float 弧線:Arc2D、Arc2D.Double、Arc2D.Float

164 基本 2D 繪圖 曲線:CubicCurve2D、CubicCurve2D.Double、CubicCurve2D.Float、QuadCurve2D、QuadCurve2D.Double、QuadCurve2D.Float 橢圓形:Ellipse2D、Ellipse2D.Double、Ellipse2D.Float 矩形:Rectangle2D、Rectangle2D.Double、Rectangle2D.Float

165 基本 2D 繪圖 圓角矩形:RoundRectangle2D 、RoundRectangle2D.Double 、RoundRectangle2D.Float 組合圖形:GeneralPath (以上列基本圖形組合成的圖形)

166 基本 2D 繪圖 要建立這些圖形物件, 需視圖形種類提供不同數量的參數。以最簡單的直線線段為例, 就只需以 2 個端點的座標點為參數;若是矩形的話, 則是指定左上角的座標, 然後指定寬與高 (若寬等於高, 就是正方形);橢圓形也類似, 需指定橢圓形外切矩形的左上角座標及寬與高 (若寬等於高, 就是圓形)。

167 基本 2D 繪圖 要繪製這類圖形, 最簡單的方式就是直接建立物件後就用 Graphics2D 的 draw() 方法畫出, 此時 Java 會以預設的畫筆來畫出圖形, 如以下的範例:

168 基本 2D 繪圖

169 基本 2D 繪圖

170 基本 2D 繪圖

171 基本 2D 繪圖 第 2 行 import java.awt.geom.*, 因程式中用到的各種 Java 2D 圖形類別均屬於此套件。
第 7〜28 為重新改寫的 paintComponent() 方法。

172 基本 2D 繪圖 第 8 行呼叫上層物件的 paintComponent() 方法, 讓上層物件重繪。若不做此動作, 則調整視窗大小時, 不一定會重繪整個視窗, 如此可能使視窗內留有先前所畫的圖形。 第 12、13 行呼叫 getSize() 方法取得面板的大小。 第 17〜27 行即繪製 4 種基本圖形的程式碼。

173 畫筆與填滿樣式 使用預設的畫筆樣式稍微單調了點, 若想做一些變化, 則可改變繪圖時的畫筆粗細、顏色, 也可設定封閉圖形中要填滿的顏色或圖樣。
要設定畫筆的屬性, 需使用 BasicStroke 類別建立畫筆物件, 然後再呼叫 Graphics2D 的 setStroke() 方法將它設為目前要用的畫筆。

174 畫筆與填滿樣式 BasicStroke 類別有數種不同形式的建構方法, 例如:

175 畫筆與填滿樣式 cap 參數可設定為以下三種: join 參數也有三種: CAP _ BUTT (預設一般筆尖) 、
CAP_ROUND (圓形筆尖)、 CAP_SQUARE (方形筆尖) join 參數也有三種: JOIN_BEVEL、 JOIN_MITER、 JOIN_ROUND 。

176 畫筆與填滿樣式 JOIN_BEVEL 表示不在交叉處做任何處理, 後兩者則分別表示用圓弧或矩形來美化交叉處。我們用一個簡單的範例來示範這幾種樣式的差異:

177 畫筆與填滿樣式

178 畫筆與填滿樣式

179 畫筆與填滿樣式

180 畫筆與填滿樣式

181 畫筆與填滿樣式 第 15、21、27 行分別以不同的樣式建立 BasicStroke 畫筆物件, 然後再呼叫 Graphics2D 的 setStroke() 方法將它設為目前所用的畫筆。 第 17〜18、23〜24、29〜30 行分別在設定新畫筆後, 畫 2 條會交會的線段, 以顯示各 JOIN_XXX 樣式的效果。

182 畫筆與填滿樣式 若要設定顏色, 其用法其實和設定畫筆類似:先建立顏色物件, 再指定 Graphics2D 使用此顏色物件, 接下來繪製的圖案就會使用指定的顏色了。 要建立顏色物件最簡單的方式直接取用 Color 類別所定義的 static 庫存顏色, 這在前面的 ChangeColor.java 範例中已使用過了, 例如:

183 畫筆與填滿樣式

184 畫筆與填滿樣式 Color 類別中定義的顏色只有 13 種, 若想使用其它的顏色就必須自行以紅 (r)、綠 (g)、藍 (b) 三原色的值來呼叫 Color 類別的建構方法, 建立自訂的顏色物件:

185 畫筆與填滿樣式 這種顏色組合法和電視、電腦螢幕產生彩色的原理一樣, 將紅綠藍三原色的電子槍調整為不同的強度, 組合出不同的顏色, 例如:

186 畫筆與填滿樣式 建立 Color 物件後, 再以其為參數, 呼叫 Graphics2D 的 setPaint() 方法, 接下來畫的圖案或線條就是使用此 Color 物件。如果想設定封閉圖案中所填滿的顏色, 例如畫一個內部都是紅色的矩形, 則需改用 Graphics2D 的 fill() 方法取代 draw() 方法來繪製圖形, 例如:

187 畫筆與填滿樣式 但若要填入較複雜的顏色漸層, 則需使用 GradientPaint 類別。要建立漸層式的 GradientPaint 類別物件很簡單, 其原理是指定 2 個點座標, 並指定 2 個點的顏色, 此時 Graphics2D 就會自動將 2 點之間以漸層的方式塗滿:

188 畫筆與填滿樣式 建好 GradientPaint 物件後, 同樣呼叫 Graphics2D 的 setPaint() 方法, 接著以fill() 方法畫的圖案, 其內部就會填上指定的漸層顏色。 以上幾個著色的應用, 請參見以下的範例:

189 畫筆與填滿樣式

190 畫筆與填滿樣式

191 畫筆與填滿樣式

192 畫筆與填滿樣式

193 畫筆與填滿樣式 第 15、20、24 行分別呼叫 Graphics2D 的 setPaint() 方法設定目前繪圖所用的顏色或漸層樣式。
第 20 行以自訂紅綠藍三色強度的方式建立 Color 物件。 第 32 行呼叫繼承自 JComponent 的 setBackground 方法將背景設為白色。

194 顯示影像 影像的存取和處理, 雖然是比較進階的主題, 但單只是用 Java 2D 所提供的影像功能來顯示硬碟中的影像檔, 並不困難。
要用 Graphics2D 顯示影像檔, 基本上需進行以下動作: 取得影像。 設定將影像顯示到畫面上時的縮放方式、比例。 顯示影像。

195 取得影像 只是單純的取得影像時, 並不需用到輸入串流, 可直接使用 AWT 所提供的 Toolkit 工具類別物件。
Toolkit 提供了一些基本的繪圖工具方法, 例如可取得螢幕目前的解析度、甚至可用它來自訂 AWT 元件的繪製方式。不過在此只需用到其 getImage() 方法來取得影像, 方式如下:

196 取得影像

197 設定縮放比例 Java 2D API 中有個特殊的 AffineTransform 類別, 可用來設定座標系統轉換的方式, 因此可將圖形和影像做放大/ 縮小、變型/ 扭曲等變化。雖然單純顯示影像並不一定要縮放或變型, 但呼叫 Graphics2D 顯示影像的方法時, 一定要指定 AffineTransform 物件為參數, 所以我們仍簡單看一下如何建立此物件。

198 設定縮放比例 只要直接呼叫 AffineTransform 的建構方法, 就可建立一個 1:1 (也就是不做轉換) 的 AffineTransform 物件: 若要設定縮放, 最簡單的方式就是以 X、Y 軸的縮放比例為參數, 呼叫 AffineTransform 的 scale() 方法:

199 顯示影像 Graphics2D 的 drawImage() 方法有多種形式, 而其中最簡單為:
前 2 個參數分別是先前載入的影像物件及 AffineTransform 轉換物件, 至於第 3 個參數則要說明一下 Java 的影像處理方式。

200 顯示影像 當我們呼叫 Toolkit 的 getImage() 方法取得影像時, 程式並不會立即載入影像檔, 而是在程式第 1 次呼叫 drawImage() 方法時, 才開始載入影像, 此時 drawImage() 並不會等 Java 讀完整個影像檔內容, 而會先返回呼叫者。 因此必須有一項機制, 讓 Java 載入整個影像後, 通知繪製顯像的元件:影像已載入。

201 顯示影像 這個機制就是透過上列第 3 個參數達成, ImageObserver 介面就是用於此種非同步載入影像時取得通知的介面, AWT 的 Component 類別即有實作此介面, 因此 AWT、Swing 所有容器、元件都可用於這第 3 個參數, 例如在 JPanel 中顯示影像, 即可用此 JPanel 物件為第 3 個參數呼叫 drawImage() 方法。

202 顯示影像 以下就是一個簡單的顯示影像檔程式, 執行此程式時需在程式名稱後面加上要顯示的影像檔名稱, 接著程式就會自動載入該影像, 並顯示於視窗中。此外程式也會依目前視窗大小, 自動調整縮放的比例。

203 顯示影像

204 顯示影像

205 顯示影像

206 顯示影像

207 顯示影像 第 05 行用 Toolkit 類別的 getImage() 方法取得影像。
第 12 行取得 JPanel 的大小, 以便計算縮放影像的比例。 第 15 行建立 AffineTransform 物件。

208 顯示影像 第 18 行則是以 img 物件呼叫 getWidth()、getHeight() 方法取得影像的寬與長, 並用它們來除 JPanel 的寬與長, 取其中較小者為縮放比例。並在第 22 行更改 AffineTransform 物件的縮放比。 第 25〜39 行的 main() 方法中用 try/catch 的方式檢查使用者是否沒有在執行程式時加上影像檔名稱為參數, 若拋出例外, 即顯示程式用法的訊息。

209 顯示影像 剛執行程式顯示視窗後, 到顯示整個影像需等數秒, 但之後改變視窗大小時, 縮放影像都能立即顯示。

210 顯示影像 若想讓影像填滿整個視窗, 可做 X、Y 軸非等比例式的縮放, 只需將上述程式中第 18 、19 行計算縮放比的敘述去掉, 並在第 20 行改為讓 X、Y 軸各自依計算出的比例縮放即可:

211 18-6 綜合演練 簡易型三角函數計算器 簡易文字編輯器

212 簡易型三角函數計算器

213 簡易型三角函數計算器

214 簡易型三角函數計算器

215 簡易型三角函數計算器

216 簡易型三角函數計算器

217 簡易型三角函數計算器

218 簡易型三角函數計算器

219 簡易型三角函數計算器

220 簡易型三角函數計算器 第 5、6 行將 TrigonoCalc 類別宣告為 KeyAdapter 的子類別並實作 ActionListener 介面。 第 7〜17 行建立 Swing 容器及元件成員。第 18 行的 convert 變數是用來將角度換算成弳度的常數。

221 簡易型三角函數計算器 第 34 行將 JPanel 設為使用 GridLayout 配置管理員, 並指定為 4 列 2 行。接著在第 36〜39 行用 JPanel 物件呼叫 add() 方法將 8 個元件依序加入其中。

222 簡易型三角函數計算器 第 42〜43 行的 setMnemonic() 方法是用來設定單選鈕的快捷鍵, 讓使用者按 [Alt] 及指定按鍵, 即可選擇該單選鈕。KeyEvent.VK_D、KeyEvent.VK_R 都是 KeyEvent 類別的 static 變數, 分別代表按鍵 [D]、[R]。

223 簡易型三角函數計算器 第 46〜48 行用 ButtonGroup 類別將 deg、rad 這兩個單選鈕設為一組。第 49 行則是用 setSelected() 方法將 deg 設為預先選取的項目。 第 51、52 行將按鈕事件及輸入區的按鍵事件傾聽者都設為 this 物件。

224 簡易型三角函數計算器 第 57〜62 行為 deg 單選鈕的選取事件處理方法:當使用者選取 deg 項目時, 就將 convert 變數設為 (180/π);若 deg 變成未被選取時 (表示使用者選擇了 rad), 就將 convert 設為 1。 第 71〜73 行為按鍵事件處理方法, 使用者按下按鈕時, 即呼叫 calc() 方法進行計算。

225 簡易型三角函數計算器 第 76〜78 行為文字輸入區的按鍵事件處理方法, 當使用者按下 [Enter] 鍵時, 也呼叫 calc() 方法。

226 簡易文字編輯器 本章中我們實際介紹到的 Swing 元件雖然不是很多, 但由於元件的基本用法都相同, 不同的只是各元件的屬性及功能。
由於在使用 Windows 或其它圖形介面環境的過程, 我們都已接觸過各式各樣的元件, 所以要將這些元件應用在我們的程式中也不難。

227 簡易文字編輯器 以下我們就應用幾個 Swing 元件 (JMenuBar、JTextArea、JScrollpane), 結合第 16 章學過的字元串流, 建立一個可開啟及儲存檔案的陽春編輯器, 您會發現, 利用 Java 所提供的類別, 我們即可輕鬆完成功能強大的應用程式。

228 簡易文字編輯器

229 簡易文字編輯器

230 簡易文字編輯器

231 簡易文字編輯器

232 簡易文字編輯器

233 簡易文字編輯器

234 簡易文字編輯器

235 簡易文字編輯器 第 6〜9 行將程式的 MyEditor 類別宣告為 JFrame 的子類別, 並包含 JTextArea (編輯區)、JFileChooser (檔案選擇交談窗) 等 2 個成員。 第 18〜24 行為 MyEditor 建構方法, 其中將文字編輯區加到 JScrollPane, 再將 JScrollPane 物件加入視窗, 如此文字編輯區就會自動具有捲軸的功能。

236 簡易文字編輯器 此外在第 22 行加入視窗的元件則是 buildmenu() 方法傳回的 JMenuBar 物件 (功能表欄)。

237 簡易文字編輯器 先建立此方法要傳回的 JMenuBar 物件。 將檔案功能表加到 JMenuBar 物件。
將三個功能表項目 (JMewu Item 物件) 加到檔案功能表。前面提過, JMenuItem 是 AbstractButton 的子類別, 所以使用者選擇功能表項目時, 也是產生按鈕事件, 所以程式中同時以匿名類別的方式, 加入三個按鈕事件處理方法。

238 簡易文字編輯器 第 64〜76 行的 readfile() 方法, 會在使用者選擇開啟檔案時, 用 JFileChooser 物件呼叫 showOpenDialog() 方法顯示開啟檔案交談窗, 參數 this 表示交談窗的父視窗是 MyEditor。第 66 行檢查使用者是否有選擇檔案, 有的話就建立 FileReader 串流物件, 並呼叫 JTextArea 的 read() 方法來讀取檔案。

239 簡易文字編輯器 第 78〜89 行的 writefile() 內容和 readfile() 類似, 只是讀取的動作換成寫入的動作。
只要學會本章提過的基本元件用法, 再來應用這些新元件都很容易上手。例如本範例中設定功能表項目時, 利用了 KeyEvent.XXX 常數來設定快捷鍵, 也使用了匿名類別來設定功能表項目被選取時的處理方法。


Download ppt "第 18 章 圖形使用者介面."

Similar presentations


Ads by Google