1 / 133

圖形程式設計簡介

圖形程式設計簡介. 簡介. 圖形介面 (Graphical User Interface ;簡稱 GUI ) GUI 程式的開發主要使用 java.awt (簡稱 AWT )或者 javax.swing (簡稱 Swing ) 目前的主流是 Swing ,最主要原因有幾個: 一個是 AWT 的類別並不是全部都使用 Java 來開發的,所以在不同的作業系統上執行的時候,同一個程式可能會有不同的行為,這對於當初設計 Java 的理念 —“ 開發一次、到處都可用” — 有衝突 因此到了設計 Swing 的時候,其全部的類別都是以 Java 開發而成

leena
Download Presentation

圖形程式設計簡介

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 圖形程式設計簡介

  2. 簡介 • 圖形介面 (Graphical User Interface;簡稱 GUI) • GUI 程式的開發主要使用 java.awt(簡稱AWT)或者javax.swing(簡稱Swing) • 目前的主流是 Swing,最主要原因有幾個: • 一個是 AWT 的類別並不是全部都使用 Java 來開發的,所以在不同的作業系統上執行的時候,同一個程式可能會有不同的行為,這對於當初設計 Java 的理念—“開發一次、到處都可用”—有衝突 • 因此到了設計 Swing 的時候,其全部的類別都是以 Java開發而成 • 另一個主因是 Swing 提供的圖形介面比 AWT 更豐富、也更細膩

  3. 視窗程式設計 • 兩大主軸: • GUI 元件之間的關係,以及 GUI 元件的使用 • 事件處理機制 • 事件處理機制大多以 inner class 來完成

  4. 視窗元件架構圖

  5. GUI元件 • 上圖左邊的視窗就是一個 JFrame 的物件 • 而一個 JFrame 的物件可以包含一個工具列的物件以及一個 Content Pane 的物件 • Content Pane 物件就像是一個容器,在這個容器中,我們可以加上各種 GUI 元件,例如標籤(JLabel)、文字欄位(JTextField)、按鈕(JButton)等 • 在 Content Pane 裡面,我們也可以放置其他的容器元件,例如 JPanel • 也就是說,一個 Content Pane 的物件可以包含多個像是JPanel 的元件,而每一個 JPanel 元件內,又可以放上像是標籤、文字欄位、或者按鈕類的 GUI 元件

  6. JFrame 類別 • 一個視窗是一個JFrame 的物件,一個最簡單的視窗程式如下所示 01 import javax.swing.*; 02 03 public class TestFrame1 { 04 public static void main( String[] args ) { 05 JFrame f = new JFrame(); 06 f.setVisible(true); 07 } 08 }

  7. setDefaultCloseOperation() • 前一個視窗,按 ”X” ,視窗不見了,卻無法把程式停掉! • 原因:缺少了”事件處理機制” import javax.swing.*; public class TestFrame2 { public static void main( String[] args ) { JFrame f = new JFrame(); f.setVisible(true); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } }

  8. 不用setDefaultCloseOperation()卻能關閉視窗程式的方式 01 import javax.swing.*; 02 import java.awt.event.*; 03 04 public class TestFrame3 { 05 public static void main( String[] args ) { 06 JFrame f = new JFrame(); 07 f.setVisible(true); 08 09 f.addWindowListener( 10 new WindowAdapter() { // inner class and adapter class 11 public void windowClosing( WindowEvent e ) { 12 // 可加入檔案關閉、視窗位置等敘述 13 System.exit( 0 ); } } ); 14 15 } 16 } 大多數的 Swing 程式都採用這種方式,因為比較有彈性。我們晚一點解釋

  9. 視窗標題的設定、視窗的大小、以及視窗出現的位置視窗標題的設定、視窗的大小、以及視窗出現的位置 01 import javax.swing.*; 03 public class TestFrame4 { 04 public static void main( String[] args ) { 05 JFrame f = new JFrame(); 06 07 f.setTitle("Hello World"); 08 f.setSize( 400, 300); 09 f.setLocation(100, 150); 11 f.setVisible(true); 12 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 13 } 14 }

  10. 執行畫面

  11. View 類別 • 常見的視窗程式設計的方式:設計 View 類別(JFrame 的子類別),其主要的功能在於提供輸入或者輸出的界面 import javax.swing.*; public class TestFrame5 extends JFrame { public TestFrame5() { setTitle("Hello World"); setSize( 400, 300); setLocation(100, 150); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main( String[] args ) { TestFrame5 f = new TestFrame5(); f.setVisible(true); } }

  12. 範例 • 設計一個視窗程式讓使用者輸入一個 Worker 的工作時數,而且在工作時數輸入完成後,由 Worker 物件計算薪資,最後由程式把薪資顯示到螢幕上 • 我們需要在把 GUI 元件加到 JFrame 內,用到的 GUI 元件包含標籤(JLabel)、文字欄位(JTextField)、以及按鈕(JButton) • 如 GUI 元件架構的說明,我們不能把 GUI 元件直接加到 JFrame 上,而是要加到 JFrame的 content pane 上

  13. GUI 畫面

  14. GUI 容器 • GUI 容器都有自己的 content pane • 例如 JFrame、JApplet、以及JDialog • 找出該容器的 content pane 需經由呼叫該容器的 getContentPane() 取得 • 例如,若 f 代表一個 JFrame 的物件,則 • Container cp = f.getContentPane(); • 在取得 content pane 的物件後,我們就可以經由 content pane 的 add() 方法把 GUI 元件放到視窗內

  15. 範例 04 public class TestGUI1 extends JFrame { 05 private Container cp; 06 private JLabel inpLabel, outLabel; 07 private JTextField input, output; 08 private JButton button; 09 10 public TestGUI1() { 11 setTitle("薪資計算"); 12 setSize(250, 200); 13 setLocation(100, 150); 14 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 15 16 cp = getContentPane(); 17 inpLabel = new JLabel("工作時數"); 18 input = new JTextField(20); 19 outLabel = new JLabel("薪資"); 20 output = new JTextField(20); 21 button = new JButton("計算薪資");

  16. 範例 22 23 cp.setLayout(new FlowLayout()); //default:BorderLayout 24 cp.add(inpLabel); 25 cp.add(input); 26 cp.add(outLabel); 27 cp.add(output); 28 cp.add(button); 29 } 30 31 public static void main( String[] args ) { 32 TestGUI1 f = new TestGUI1(); 33 f.setVisible(true); 34 } 35 } 續上頁程式

  17. 範例 • 執行 TestGUI1 • 我們可以在”工作時數”欄位輸入資料 • 但是,不論是按 Enter 或者是在按鈕上點一下,並不會發生任何事情! • 對於版面管理員(Layout Manager)有興趣的,可以試著變更視窗的大小,並觀察 GUI 元件擺放位置的變化。 • JFrame 的預設 Layout 是 BorderLayout。

  18. 事件處理機制 • 我們先解釋事件(event) • 所謂的事件,指的是所有發生在 GUI 元件上的使用者行為 • 例如,在按鈕上按(click)一下,在文字欄位在按”Enter”鍵,在視窗上移動滑鼠等,都是一個個的事件 • 這種由於發生事件的不同,而決定程式行為的模式,一般稱之為”事件驅動模式”(event-driven)

  19. 委任式的事件處理模式(Delegation-based event model) • 在委任式的事件處理模式中,有兩種主要的參與者 • 一種是原始事件物件(event source objects) • 還有一個隱藏的監控者 • 另一種是事件聆聽者物件(event listener objects) • 所謂的原始事件物件指的是事件發生的那個 GUI物件 • 例如,使用者在按鈕 A 上按了一下,則按鈕 A 即為原始事件物件 • 例如,使用者在文字欄位上輸入資料後,按了”Enter”鍵,則該文字欄位就是原始事件物件

  20. 委任式的事件處理模式 • 在應用程式中,原始事件物件可能會有多個,但是並不是每一個原始事件物件都要參與事件處理 • 例如:我們的範例中,我們可以只讓按鈕參與。 • 想要參與事件處理的原始事件物件必須註冊 • 註冊的時候,我們必須同時註明處理該事件的事件聆聽者物件

  21. 委任式的事件處理模式 • 事件聆聽者物件是一個繼承 EventListener 的物件 • java.util.EventListener 是一個 Interface,是所有事件聆聽者物件(或者類別)的父類別 • 事件聆聽者物件的資料型態會隨著事件的不同,而有不同的資料型態 • 例如:若原始事件物件是一個 JButton 的物件,則事件的類別是 java.awt.event.ActionEvent,而對應於 ActionEvent 的事件聆聽者物件是一個 java.awt.event.ActionListener 的物件

  22. 委任式的事件處理模式 • JButton 的事件聆聽者物件必須繼承ActionListener;由於 ActionListener 是一個 Interface,且該介面型態含有一個 actionPerformed()的方法,因此該事件聆聽者物件也必須實作出 actionPerformed() • 請記住,每一個 GUI 元件都可以有一個以上的事件聆聽者物件,至於該事件聆聽者物件屬於何種 EventListener,在 Java 中都有一定的規定,而且每一種 EventListener 都有其特定的抽象方法必須實作出來

  23. 註冊(register)流程

  24. 監控者(monitor) • 負責隨時監控事件的發生 • 一旦一個事件發生了,它知道”事件發生在哪一個原始事件物件”,然後依據之前註冊的原始事件物件呼叫對應的事件聆聽者物件方法 • 由於事件的處理是由事件聆聽者物件來進行,所以才稱為委任式事件處理模式(由監控者委任事件聆聽者物件完成事件處理) • 可以把監控者物件當成 JVM 的一部份功能

  25. 事件觸發啟動流程

  26. JButton 註冊的方式 • 由於 JButton 的事件聆聽者物件屬於ActionListener,所以註冊的方式如下 • button.addActionListener(ActionListener 物件); • button 是 JButton 物件 • “ActionListener 物件”的對應類別必須是一個 implements ActionListener 的類別

  27. 原始事件物件、事件聆聽者物件、以及監控者三者的關係

  28. JButton 的事件聆聽者 • 我們希望在使用者在 JButton 上點一下的時候,能夠”取得使用者輸入的工作時數,並經由 Worker 物件計算出薪資“ • 經由之前的討論,我們知道這些動作必須由 actionPerformed() 來完成。 • 假設 JButton 的事件聆聽者的類別名稱為SalaryHandler,其設計方式如下頁。

  29. JButton 的事件聆聽者 • SalaryHandler 01 import javax.swing.*; 02 import java.awt.*; 03 import java.awt.event.*; 04 05 public class SalaryHandler implements ActionListener { 06 public void actionPerformed(ActionEvent e) { 07 JButton button = (JButton) e.getSource(); 08

  30. JButton 的事件聆聽者 • 續上頁的程式 09 // 從 button 找出它所屬的 content pane 10 JRootPane rp = button.getRootPane(); 11 Container cp = rp.getContentPane(); 13 JTextField input = (JTextField) cp.getComponent(1); 14 int h = Integer.parseInt(input.getText()); 15 Worker w = new Worker(h); 16 JTextField output = (JTextField) cp.getComponent(3); 17 output.setText("薪資為 " + w.computeSalary()); 18 } 19 }

  31. Root Pane vs. Content Pane 資料來源:http://java.sun.com/docs/books/tutorial/uiswing/components/toplevel.html#rootpane

  32. GUI 元件之間的關係 • 經由 ActionEvent,我們可以知道事件發生在 JButton • 但是 JButton 和 JTextField 彼此獨立;也就是說 JButton 不知道 JTextField 是否存在 • 目前只有 Content Pane 知道它自己包含了哪些 GUI 元件 • 還記得我們之前將 GUI 元件加到 Content Pane 的過程? • 但是,JButton 無法直接取得 Content Pane,但卻可以取得 Root Pane;而因為 Root Pane 管理 Content Pane,因此可以經由 Root Pane 取得 Content Pane。

  33. 範例 • TestGUI2,其程式碼如下: 01 import javax.swing.*; 02 import java.awt.*; 03 04 public class TestGUI2 extends JFrame { 05 private Container cp; 06 private JLabel inpLabel, outLabel; 07 private JTextField input, output; 08 private JButton button; 09 10 public TestGUI2() { 11 setTitle("薪資計算"); 12 setSize(250, 200); 13 setLocation(100, 150); 14 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  34. 16 inpLabel = new JLabel("工作時數"); 17 input = new JTextField(20); 18 outLabel = new JLabel("薪資"); 19 output = new JTextField(20); 20 button = new JButton("計算薪資"); 21 22 cp = getContentPane(); 23 cp.setLayout(new FlowLayout()); 24 cp.add(inpLabel); 25 cp.add(input); 26 cp.add(outLabel); 27 cp.add(output); 28 cp.add(button); 29 30 // register 31 button.addActionListener(new SalaryHandler()); 32 } 33 34 public static void main( String[] args ) { 35 TestGUI2 f = new TestGUI2(); 36 f.setVisible(true); 37 } 38 } 範例 續上頁的程式

  35. 執行畫面

  36. 事件聆聽者的設計方法 • 單獨設計一個獨立的事件聆聽者雖然可以得到”模組化“的好處,但是在取得視窗內 GUI 元件的方式卻稍嫌繁雜! • 設計事件聆聽者的方式還有兩種: • 一種是利用 inner class 的架構(這是最常見的方式) • 另一種是把原始事件物件以及事件聆聽者物件結合的架構

  37. inner class • inner class 的成員類別(member class) • 使用成員類別的目的就是希望把之前的SalaryHandler 的類別宣告在 TestGUI2 內 • 為了清楚說明這個用法,我們把 TestGUI2修改成 TestGUI3,然後把 SalaryHandler宣告成 TestGUI3 的成員

  38. 範例 只呈現出重點程式碼 40 private class SalaryHandler implements ActionListener { 41 public void actionPerformed(ActionEvent e) { 42 int h = Integer.parseInt(input.getText()); 43 Worker w = new Worker(h); 44 output.setText("薪資為 " + w.computeSalary()); 45 } 46 } 主要的差異:使用 inner class,可以簡化取得 GUI 中其他元件的步驟。

  39. 第三種方式 • TestGUI 是 JFrame,同時也是ActionListener 05 public class TestGUI4 extends JFrame implements ActionListener { ….. 31 // register 32 button.addActionListener(this); 40 public void actionPerformed(ActionEvent e) { 41 int h = Integer.parseInt(input.getText()); 42 Worker w = new Worker(h); 43 output.setText("薪資為 " + w.computeSalary()); 44 }

  40. 第二種方式的變形 註冊 WindowListener 01 import javax.swing.*; 02 import java.awt.event.*; 03 04 public class TestFrame3 { 05 public static void main( String[] args ) { 06 JFrame f = new JFrame(); 07 f.setVisible(true); 08 09 f.addWindowListener( 10 new WindowAdapter() { // inner class and adapter class 11 public void windowClosing( WindowEvent e ) { 12 // 可加入檔案關閉、視窗位置等敘述 13 System.exit( 0 ); }} ); 14 15 } 16 } 使用匿名 inner class

  41. 練習題 • 請使用匿名的內隱類別的方式來改寫 TestGUI3 程式。 • 請修改 TestGUI 的畫面,使得使用者可以選擇 H/S (HourlyWorker/SalaryWorker) 並為該物件計算薪資?

  42. 練習題 • 繼續之前 Circle/Rectangle 的範例, • 請設計一套視窗系統,使得使用者可以從檔案 shape.txt 讀取任意一個 Circle 或者 Rectangle 物件,檔案格式如下: • C 4.2 • R 1.2 4.5 • C 3.1 • C 代表 cirlce,而 R 代表 rectangle;請依照之前的範例,將讀取的物件的面積、周長等資料列印出來 • 執行後的第一個畫面會出現第一筆,畫面底下有”下一筆”和”上一筆”的按鈕,讓使用者可以檢視所有資料 • 在第一筆的時候,能不能按”上一筆”? • 在最後一筆的時候,能不能按”下一筆”?

  43. 兩個以上的事件處理 • TestGUI 還有改進的地方: • 若需要再次計算薪資,之前的資料還在,能不能有個”清除”按鈕來清除資料? • 我們每一次輸入完資料後,一定要按”計算薪資”按鈕才會計算薪資,可不可以把它設計成輸入完資料後直接按”Enter”鍵即可計算? • 我們先以第二種方式來說明

  44. 兩個以上的事件處理 • 按鈕的部分: • 設計 compButton 和 clrButton 兩個 JButton 物件分別代表”計算薪資”和”清除”按鈕 • 必須將 clrButton 註冊 • 會發生在 compButton 和 clrButton 上的事件都是 ActionEvent,而處理該種事件的事件聆聽者物件的資料型態是 ActionListener,而ActionListener 的事件處理方法為actionPerformed()

  45. 範例 (JTextField) • 文字欄位(JTextField)部分 • 可以發生在 JTextField 的事件為何? • 在下兩頁表格(課本的第 14 章附錄)的第二欄我們知道 JTextField 的事件聆聽者物件的資料型態是ActionListener • 從第三個欄位,我們知道該事件介面的事件處理方法是 actionPerformed(),而從事件處理方法的參數,就可以知道該事件的資料型態是 ActionEvent • 從第四欄位知道,該事件常用的方法有 getSource()和 getActionCommand()

  46. 附錄

  47. 附錄

  48. 範例 • 三個原始事件物件都需要註冊 • 事件聆聽者物件的資料型態都是ActionListener,因此可以設計三個ActionListener 的子類別;分別是 • InputHandler • CompButtonHandler • 以及 ClrButtonHandler

  49. InputHandler private class InputHandler implements ActionListener { public void actionPerformed(ActionEvent e) { int h = Integer.parseInt(input.getText()); Worker w = new Worker(h); output.setText("薪資為 " + w.computeSalary()); } }

  50. CompButtonHandler private class CompButtonHandler implements ActionListener { public void actionPerformed(ActionEvent e) { int h = Integer.parseInt(input.getText()); Worker w = new Worker(h); output.setText("薪資為 " + w.computeSalary()); } }

More Related