圖形程式設計簡介.

Slides:



Advertisements
Similar presentations
3.2 Java的类 Java 类库的概念 语言规则——程序的书写规范 Java语言 类库——已有的有特定功能的Java程序模块
Advertisements

第一單元 建立java 程式.
JAVA程序设计 天津工程职业技术学院计算机工程系软件技术教研室.
Java程序设计教程 第一讲 Java概述.
視窗程式設計 2. 視窗版面配置 Chih Hung Wang Reference:
第 18 章 圖形使用者介面.
第三部分 Java语言编程应用篇 第6章 Java语言的 图形用户界面开发技术 (之二).
PowerPoint圖形總合.
Ch06 人機介面_視窗程式設計(2) 物件導向系統分析與設計.
第9章 图形用户界面(GUI)设计.
Ch02 視窗Swing套件 物件導向系統實務.
Java语言程序设计-图形用户界面设计(2)
程式設計概論 1.1 程式設計概論 程式語言的演進 物件導向程式 程式開發流程 1.2 C++開發工具
2018/11/15 面向对象与多线程综合实验-GUI设计 教师:段鹏飞.
9/28號專題報告 Web網頁遊戲 曾建瑋.
Q101 在701 SDX Linux上的標準安裝與使用程序v2
JDK 安裝教學 (for Win7) Soochow University
SWING 2018/11/23.
客戶端的檔案上傳 HtmlInputFile檔案控制項 上傳單一檔案 同時上傳多個檔案.
記事本 物件導向系統實務.
桌面環境簡介及IDE開發工具 Outline (一)什麼是Linux? (二)桌面環境系統簡介 (三)IDE開發工具.
類別(class) 類別class與物件object.
SQL Stored Procedure SQL 預存程序.
記事本 物件導向系統實務.
安裝JDK 安裝Eclipse Eclipse 中文化
Word與PowerPoint的結合 建功國小 陳旻杰 健行國小 張慧如.
Visual Basic 物件導向程式設計簡介.
AWT Event Model.
系統設定 IE8相容性檢視
Ch04 事件處理 物件導向系統實務.
CH03 為自己的視窗加上小元件 物件導向系統實務.
Java 程式設計 講師:FrankLin.
網頁切換移轉 JS vs. ASP.NET.
電腦攻擊與防禦 使用電腦教室VMware軟體說明.
CH04 視窗中元件排排坐 物件導向系統實務.
事件處理.
第一單元 建立java 程式.
標籤、按鈕、工具列、狀態列 (Labels, Buttons, Tool Strips, and Status Strips)
VS.NET 2003 IDE.
Ch20. 計算器 (Mac 版本).
Multithread 多執行緒 以GUI為例了解物件以及Event
第 19 章 XML記憶體執行模式.
HTML – 超連結與圖片 資訊教育.
網頁程式概論 建國科技大學資管系 饒瑞佶 2015/9 V1 2016/4 V2 2016/9 V3.
表格(HTML – FORM).
安裝 / 操作 flashget SOP (以Win 7 作業系統為範例)
GridView.
GridView操作 (II).
如何使用Gene Ontology 網址:
CVPlayer下載及安裝& IVS操作說明
FTP使用教學 簡介: 軟體名稱:FileZilla 軟體性質:Freeware 版本: 繁體中文版
Video 影像 (VideoPlayer 影像播放器、Camcorder 錄影機) 靜宜大學資管系 楊子青
流程控制:Switch-Case 94學年度第一學期‧資訊教育 東海大學物理系.
取得與安裝TIDE 從TIBBO網站取得TIDE
MiRanda Java Interface v1.0的使用方法
Dreamweaver 進階網頁製作 B 許天彰.
1. 查詢個人電腦版本 1.進入控制台 2.點選“所有控制台項目” 3.點選“系統”.
進階UI元件:ListView元件以及複選 靜宜大學資管系 楊子青
5. 令圖畫動起來 Tween 功能介紹 移動效果 顏色漸變效果 形狀漸變效果 離開.
如何從政大圖書館館藏目錄匯出書目至EndNote
Brief Guide of FrontPage
班級:博碩子一甲 授課老師:鐘國家 助教:陳國政
Parasitics Extraction (PEX) 與 postsimulation(posim)
多國語系 建國科技大學 資管系 饒瑞佶.
String類別 在C語言中提供兩種支援字串的方式 可以使用傳統以null結尾的字元陣列 使用string類別
NFC (近場通訊, Near Field Communication) 靜宜大學資管系 楊子青
Chapter 4 Multi-Threads (多執行緒).
Unix指令4-文字編輯與程式撰寫.
Develop and Build Drives by Visual C++ IDE
InputStreamReader Console Scanner
Presentation transcript:

圖形程式設計簡介

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

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

視窗元件架構圖

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

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 }

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); }

不用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 程式都採用這種方式,因為比較有彈性。我們晚一點解釋

視窗標題的設定、視窗的大小、 以及視窗出現的位置 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 }

執行畫面

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);

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

GUI 畫面

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

範例 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("計算薪資");

範例 續上頁程式 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 }

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

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

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

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

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

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

註冊(register)流程

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

事件觸發啟動流程

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

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

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

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

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 }

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

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。

範例 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);

範例 續上頁的程式 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 } 範例 續上頁的程式

執行畫面

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

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

範例 只呈現出重點程式碼 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 中其他元件的步驟。

第三種方式 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 }

第二種方式的變形 01 import javax.swing.*; 02 import java.awt.event.*; 03 註冊 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

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

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

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

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

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

附錄

附錄

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

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()); }

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()); }

討論 由於上述兩個類別(InputHandler 及 CompButtonHandler)的內容完全相同

clrButtonHandler private class ClrButtonHandler implements ActionListener { public void actionPerformed(ActionEvent e) { input.setText(""); output.setText(""); }

註冊 結合了以上兩種 ActionListener 的子類別,input、compButton、和 clrButton 需要分別向監控者註冊,註冊的方式如下: 兩種方式是否有差異? InputHandler ih = new InputHandler(); input.addActionListener(ih); compButton.addActionListener(ih); clrButton.addActionListener(new ClrButtonHandler()); 以上的程式碼也可以用下列的程式碼取代: input.addActionListener(new InputHandler()); compButton.addActionListener(new InputHandler()); clrButton.addActionListener(new ClrButtonHandler());

範例 把 TestGUI3 和 InputHandler 以及 ClrButtonHandler 結合之後,我們完成 TestGUI5;需要修改的程式碼如下所示(僅列出重點): private JButton compButton, clrButton; clrButton = new JButton("清除"); // register InputHandler ih = new InputHandler(); input.addActionListener(ih); compButton.addActionListener(ih); clrButton.addActionListener(new ClrButtonHandler());

續上頁程式 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()); } private class ClrButtonHandler implements ActionListener { input.setText(""); output.setText("");

練習題 請使用第一種方式來完成 TestGUI5。

使用第三種方式 01 public void actionPerformed(ActionEvent e) { 02 if(e.getSource() instanceof JButton) { 03 String type = e.getActionCommand(); 04 if(type.equals("計算薪資")) 05 compSalary(); 06 else { 07 input.setText(""); 08 output.setText(""); 09 } 10 } else { 11 compSalary(); 12 } 13 } 14 15 private void compSalary() { 16 int h = Integer.parseInt(input.getText()); 17 Worker w = new Worker(h); 18 output.setText("薪資為 " + w.computeSalary()); 19 }

練習題 請問是否能進一步簡化第二種方式(作法如第三種方式,將類別減少成一個)來完成 TestGUI5。

三種方式的比較 三種方式都有人使用 我個人比較喜歡第一或者二種方式 第三種方式在 GUI 元件越來越多的情形下,需要判斷的情形會越來越複雜,容易造成維護不易 第一、二種的重覆使用性比較高,而且處理的對象單純,容易維護。 第二種的變形方式只適合於簡單的事件處理。

版面管理員 一個 GUI 容器內的版面管理員負責排列該版面的 GUI 元件 Java 支援的版面管理員包含 java.awt.BorderLayout javax.swing.BoxLayout java.awt.CardLayout java.awt.FlowLayout java.awt.GridBagLayout java.awt.GridLayout javax.swing.SpringLayout

BorderLayout 把版面分成北、南、東、西、以及中央的五個區塊 特性 如果我們將調整視窗大小,調整之後,東邊和西邊兩個區塊的長度(Y 軸)就會變大或變小,但是寬度(X 軸)不變 而南邊和北邊兩個區塊,在調整後的長度不變,但是寬度變寬或者變窄 而中央區塊,不論是長或者寬,都會跟著視窗的改變而改變

範例 我們將視窗依照預設的方式分成東西南北中等五個區塊,每一個區塊加上一個JButton的按鈕 11 Container cp = getContentPane(); 12 cp.setLayout(new BorderLayout()); 13 cp.add(new JButton("北邊"), BorderLayout.NORTH); 14 cp.add(new JButton("南邊"), BorderLayout.SOUTH); 15 cp.add(new JButton("東邊"), BorderLayout.EAST); 16 cp.add(new JButton("西邊"), BorderLayout.WEST); 17 cp.add(new JButton("中央"), BorderLayout.CENTER);

執行結果

練習題 請問 TestGUI 適不適合單獨使用 FlowLayout 或者 BorderLayout?

GridLayout GridLayout 是把版面分成表格狀 我們把視窗分成一個 3x2 的表格,那麼這個視窗就變成一個三列,而每一列有兩個欄位的表格,也就是總共有六個區塊 在預設的情形下,每一個被加入的 GUI 元件都是以由左而右,由上而下的順序來擺放的

範例 我們將視窗依照預設的方式分成 3x2 的六個區塊,每一個區塊加上一個 JButton 的按鈕 11 Container cp = getContentPane(); 12 cp.setLayout(new GridLayout(3,2)); 13 cp.add(new JButton("1")); 14 cp.add(new JButton("2")); 15 cp.add(new JButton("3")); 16 cp.add(new JButton("4")); 17 cp.add(new JButton("5")); 18 cp.add(new JButton("6"));

執行結果

練習題 請問 TestGUI 適不適合單獨使用 FlowLayout、 BorderLayout、或者 GridLayout?

版面管理員與 JPanel

版面管理員 從上頁的說明,單獨使用某一特定的版面管理員很容易造成位置錯置或者擺放不好看的情形 最常見的作法: 不建議的做法:不要任何版面管理員(即 cp.setLayout(null);),並設定視窗無法變更大小、所有 GUI 元件的大小、位置也都固定 最常見的作法: 利用某一版面管理員把視窗分成幾個區塊 然後在這幾個區塊中,加上容器物件(如 JPanel) 在必要的情形下,再分別針對容器設計其版面。 最後,將必要的 GUI 元件一一加入容器

薪資範例的版面 這樣的版面,我們希望不論如何改變視窗的大小,按鈕一定出現在視窗的底部,而標籤與其對應的文字欄位都會出現在同一行,而兩對標籤/文字欄位一定是一個在上、一個在下

版面設計 c

版面設計 在 content pane 上,我們使用 BorderLayout 物件來管理版面 我們只把整個版面分成兩個區塊,一個是北邊區塊,另一個是南邊區塊 北邊區塊,我們加入一個 JPanel 物件 cpan,然後我們在 cpan 上再套上一個 2x2 的 GridLayout版面 GridLayout 多出的 10 和 20 分別代表 GridLayout 內的 GUI 元件間的橫向以及縱向距離分別是 10 和 20 個點 而南邊的區塊,我們加入一個 JPanel 物件span,然後我們在 span 上再套上一個 1x2 的GridLayout 版面

程式碼 上頁版面的程式 01 cp = getContentPane(); 02 cp.setLayout(new BorderLayout(10,20)); 03 JPanel cpan = new JPanel(); 04 cpan.setLayout(new GridLayout(2,2,10,20)); 05 06 JPanel span = new JPanel(); 07 span.setLayout(new GridLayout(1,2,10,20)); 08 09 cp.add(cpan, BorderLayout.NORTH); 10 cp.add(span, BorderLayout.SOUTH);

程式碼 將 GUI 元件分別加入 JPanel cpan.add(inpLabel); cpan.add(input); cpan.add(outLabel); cpan.add(output); span.add(compButton); span.add(clrButton);

練習題 請修改 TestPanel1 使其 請觀察經由這樣的變化, cpan 變成 BorderLayout 的 CENTER 標籤以及文字欄位的擺放由 BorderLayout 的 EAST 和 WEST 負責 請觀察經由這樣的變化, 我們需要因為 View 的改變,而改變事件處理機制(Control)嗎? 我們需要改變 Model 類別嗎?

其它常用的GUI元件-JTextArea 就是所謂的文字區塊,一般用來讓使用者輸入較多的文字,或是讓程式顯示較多的訊息

範例 (程式重點) 10 private JTextArea textarea; 24 textarea = new JTextArea(10,15); 26 cp = getContentPane(); 27 cp.setLayout(new GridLayout(1,2,5,10)); 28 JPanel newp = new JPanel(); 29 newp.setLayout(new BorderLayout(5,10)); 30 cp.add(newp); 31 cp.add(textarea); 33 JPanel cpan = new JPanel(); 34 cpan.setLayout(new GridLayout(2,2,5,10)); 35 cpan.add(inpLabel); 36 cpan.add(input); 37 cpan.add(outLabel); 38 cpan.add(output); 39 40 JPanel span = new JPanel(); 41 span.setLayout(new GridLayout(1,2,5,10)); 42 span.add(compButton); 43 span.add(clrButton); 45 newp.add(cpan, BorderLayout.NORTH); 46 newp.add(span, BorderLayout.SOUTH); textarea.append("工作 " + input.getText() + " 小時,薪資為 " + 66 w.computeSalary() + "\n"); 在 actionPerformed() 中將輸出資料也加到 textarea 中。

JScrollPane 在 Swing 中,要使得文字方塊出現捲動軸,需要先產生一個類似 JPanel 的 JScrollPane 的物件,然後把文字方塊加到 JScrollPane 的物件內,在我們的範例程式中,我們只需要在把cp.add(textarea); 敘述換成 cp.add(new JScrollPane(textarea)); 另一件事情就是在文字區塊 append() 完輸出資料之後,程式需要加上 textarea.setCaretPosition( textarea.getDocument().getLength() ); caret 代表插入點

執行結果 為避免使用者不小心在這兩個物件上輸入資料,我們(以output為例)可以設定 output.setEditable(false);

JComboBox 一個 JComboBox 物件代表一個下拉式的選單,該選單可以允許使用者選擇其中一個選項 JComboBox 事件的類別是 ItemListener,而事件的類別是 ItemEvent,而事件處理方法是 itemStateChanged()

JComboBox 為了能在下拉式選單中出現”固定薪資員工”和”時薪員工”兩個選項,我們首先產生一個字串陣列: private final String[] WORKERS = {"固定薪資員工", "時薪員工"}; 把這個陣列當作JComboBox的引數來產生一個下拉式選單的物件types types = new JComboBox(WORKERS); 由於每一次使用者選擇不同的員工種類,都會造成標籤的不同,因此我們需要把types向監控者註冊,註冊的方式如下: types.addItemListener(new ListHandler());

程式碼 ListHandler 必須實作 ItemListener,其程式碼如下: private class ListHandler implements ItemListener { public void itemStateChanged(ItemEvent e) { if(types.getSelectedIndex() == 1) { // 選了第二項 inpLabel.setText("工作時數"); } else { inpLabel.setText("年薪"); }

程式碼 InputHandler 必須實作 ActionListener,其程式碼如下: private class InputHandler implements ActionListener { public void actionPerformed(ActionEvent e) { String n = name.getText(); int h = Integer.parseInt(input.getText()); Worker w = null; int s = types.getSelectedIndex(); if(s == 0) { w = new SalaryWorker(n, h); } else { w = new HourlyWorker(n, h); } …

練習題 請修改 TestPersonnel,使得文字方塊下出現一個”存檔“的按鈕。一旦使用者按下”存檔“按鈕,程式會出現 JFileChooser 的檔案開啟視窗讓使用者將輸入的資料儲存到某個檔案內。 若使用者選擇視窗的 “X” 來關閉程式,資料須不需要儲存?若要,如何做? 再修改 TestPersonnel,使得文字方塊下出現一個”開啟”的按鈕。一旦使用者按下”開啟“按鈕,程式會出現 JFileChooser 的檔案開啟視窗讓使用者將選定的檔案內容顯示在文字方塊內。

JCheckBox 一個 JCheckBox 物件代表一個核取方塊,一般常用於允許使用者選擇一個以上的選項用的 例如調查使用者的興趣時,可以允許使用者選擇多項興趣 能處理 JCheckBox 事件的類別是ItemListener,而事件的類別是 ItemEvent,而事件處理方法是itemStateChanged()

程式顯示四種興趣讓使用者選擇 如果使用者選擇”音樂”核取方塊,則右邊的文字方塊會出現”選取 音樂”的資料;反之,如果使用者再次選擇”音樂”核取方塊,由於該方塊之前已經被選取了,右邊的文字方塊會出現”取消 音樂”的資料

核取方塊 JCheckBox 的物件 music = new JCheckBox("音樂"); 該核取方塊的標籤為”音樂” music.addItemListener(new CheckBoxHandler());

而CheckBoxHandler必須實作ItemListener,其程式碼如下: 01 private class CheckBoxHandler implements ItemListener { 02 public void itemStateChanged(ItemEvent e) { 03 if(e.getSource() == music) 04 display(e, music); 06 if (e.getSource() == sport) 07 display(e, sport); 09 if (e.getSource() == computer) 10 display(e, computer); 12 if (e.getSource() == movie) 13 display(e, movie); 14 } 16 private void display(ItemEvent e, JCheckBox b) { 17 // 兩種情形:click 有可能是選取,也有可能是取消選取 18 if(e.getStateChange() == ItemEvent.SELECTED) 19 textarea.append("選取 " + b.getText() + "\n"); 20 else 21 textarea.append("取消 " + b.getText() + "\n"); 22 } 23 }

把剛剛的改成如下的程式碼 private class CheckBoxHandler implements ItemListener { public void itemStateChanged(ItemEvent e) { // 兩種情形:click 有可能是選取,也有可能是取消選取 if(e.getStateChange() == ItemEvent.SELECTED) textarea.append("選取 " + ((JCheckBox) e.getSource()).getText() + "\n"); else textarea.append("取消 " + ((JCheckBox) }

JRadioButton 一個 JRadioButton 物件代表一個選取鈕(Radio Buttons),一般常用於允許使用者從多個選擇項目中,選擇一個選項。 例如我們調查使用者的性別時,使用者只能選取女性或者男性 JRadioButton 事件的類別是 ItemListener,而事件的類別是 ItemEvent,而事件處理方法是 itemStateChanged()

JRadioButton 產生 JRadioButton 物件: female = new JRadioButton("女性", true); 為了確定 female 和 male 在同一組,我們的設定方式如下: sexGroup = new ButtonGroup(); sexGroup.add(male); sexGroup.add(female); 把 female 向監控者註冊: female.addItemListener(new RadioButtonHandler()); 為了讓兩個JPanel能夠很明確的區分出來,而且有適當的標題 upan.setBorder(BorderFactory.createTitledBorder("性別")) ;

RadioButtonHandler 必須實作 ItemListener,其程式碼如下 private class RadioButtonHandler implements ItemListener { public void itemStateChanged(ItemEvent e) { // 點選一個 JRadioButton 物件,會有兩個 JRadioButton 發生事件: // 一個是剛剛選取的,另一個是被 Java 取消的 if(((JRadioButton) e.getSource()).isSelected()) textarea.append("選取 " + ((JRadioButton) e.getSource()).getText()+ "\n"); else textarea.append("取消 " + ((JRadioButton) e.getSource()).getText()+ }

ToolTip 一個工具提示(ToolTip)通常用於:當滑鼠移到某個 GUI 元件時,所顯示的文字提示 設定的方式很簡單,只需要在該 GUI 元件上利用 setToolTipText() 方法來設定提示的內容

ToolTip 範例: 修改 TestPersonnel,使得在顯示”工作時數”的標籤上會顯示”請輸入該員工本月工作時數”的提示;而在”年薪”的標籤上,顯示”請輸入該員工年薪”。

ToolTip 需要修改的部分: 第一次產生 JLabel 的物件後,就要設定 每次選擇不同的 Worker 型態時,要設定 inpLabel.setToolTipText("請輸入該員工年薪"); 每次選擇不同的 Worker 型態時,要設定 private class ListHandler implements ItemListener { public void itemStateChanged(ItemEvent e) { if(types.getSelectedIndex() == 1) { // 選了第二項 inpLabel.setText("工作時數"); inpLabel.setToolTipText("請輸入該員工本月工作時數"); } else { inpLabel.setText("年薪"); }

滑鼠事件 滑鼠事件是 MouseEvent,而其相對應的事件聆聽者類別是 MouseListener。 以 TestPersonnel 為例: 假設我們希望當滑鼠移到畫面上”年薪“或者”工作時數”標籤的時候,標籤內容會分別變成”最低年薪是 $100,000”或者”每月最高工作時數是 200 小時”;當滑鼠移出該標籤位置後,標籤內容又會恢復成原來的內容。

滑鼠事件

滑鼠事件 MouseListener 總共有五個抽象方法: 依照範例的說明,我們只對後兩個有興趣,其他三個並不需要處理 mouseClicked(): 滑鼠 click 的時候(click 分別有一次 pressed 和 released) mousePressed(): 滑鼠按下去的時候 mouseReleased(): 滑鼠按下去後鬆開的時候 mouseEntered(): 滑鼠進入時 mouseExited(): 滑鼠移開時 依照範例的說明,我們只對後兩個有興趣,其他三個並不需要處理 但是因為他們都是抽象方法,我們還是要實作出來。

滑鼠事件 由 inpLabel 註冊滑鼠事件處理 MouseHandler 的內容如下: inpLabel.addMouseListener(new MouseHandler()); MouseHandler 的內容如下: private class MouseHandler implements MouseListener { public void mouseClicked(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} public void mouseEntered(MouseEvent e) { JLabel lab = (JLabel) e.getSource(); if(lab.getText().equals("年薪")) lab.setText("最低年薪是 $100,000"); else lab.setText("每月最高工作時數是 200 小時"); } public void mouseExited(MouseEvent e) { if(lab.getText().equals("最低年薪是 $100,000")) lab.setText("年薪"); lab.setText("工作時數");

MouseListener 有 MouseAdapter;WindowListener 有 WindowAdapter。 依照之前的範例說明,我們雖然只對後兩個有興趣,但是其他三個仍然要實作出來,實在沒什麼意義。 為了簡化這個情形,Java 為一些方法比較多的 Listener 設計了相對應的 Adapter 類別 簡單的說,Adapter 類別是抽象類別,但是他已經實作了其對應的 Listener 內的所有抽象方法。 基本上,這些方法的內容都是空的。 MouseListener 有 MouseAdapter;WindowListener 有 WindowAdapter。

滑鼠事件與 Adapter 類別 使用 MouseAdapter 後的程式碼如下;請留意,我們只需要實作出我們需要的 mouseEntered() 和 mouseExited()。 private class MouseHandler extends MouseAdapter { public void mouseEntered(MouseEvent e) { JLabel lab = (JLabel) e.getSource(); if(lab.getText().equals("年薪")) lab.setText("最低年薪是 $100,000"); else lab.setText("每月最高工作時數是 200 小時"); } public void mouseExited(MouseEvent e) { if(lab.getText().equals("最低年薪是 $100,000")) lab.setText("年薪"); lab.setText("工作時數");

Applet Java程式分成兩大類 我們目前所寫的 Java 程式都屬於 Java Application 另一類,我們稱它為 X-let,而所謂的 X-let 包含我們即將介紹的 applet (還有以後你們會學的 servlet) 所謂 X-let 的 Java 程式有一個特性,那就是這一類的程式必須在一個事先已經預訂好的環境下執行,以 applet 為例,所有的applet 都是在網頁內執行的 Java 程式 Applet 是一個類別檔並被設定在一個網頁內,放在伺服器端 使用者利用瀏覽器拜訪這個網頁的時候,applet 會被下載下來,然後在瀏覽器裡面的 JVM 開始執行

範例 01 import javax.swing.JApplet; 02 import java.awt.Graphics; 03 04 public class HelloSwing extends JApplet { 05 public void paint(Graphics g) { 06 g.drawString("Hello World!", 25, 25); 07 } 08 } 範例 要執行這個 applet,我們需要撰寫一個網頁檔(HTML檔),其內容如下,檔案名稱為 hello.html: <applet code="HelloSwing.class" width="300" height="100"> </applet>

執行畫面

執行畫面

applet 的生命週期 注意:X-let 都有其生命週期。只是隨著 X-let 的不同,而有各自的定義方法。

讓我們把之前的 TestRadioButton 改成 applet,需要修改的部分很少,如以下的程式碼所示 需要修改的地方不多: 改為 JApplet 的子類別 建構元的部分搬到 init()。(why?) Applet 沒有標題、”X” 等 Applet 大小由 HTML 的設定決定

Applet 的限制 由於 applet 是在瀏覽器內執行,而該 applet 大多是由遠端伺服器上下載下來的,會不會下載的 applet 是一個惡意程式(像病毒)? Applet 提供了一些基本的安全限制,在未經授權的情形下,applet 不可以讀取當地的檔案/目錄(更不要說複寫或者修改了)、只能跟下載的伺服器進行網路連線等

練習題 請試著將之前的所有視窗程式都改成 applet。

利用 NetBeans 開發視窗程式 先了解排版需求(BorderLayout, 然後 GridLayout 2x1, 最後上下都是 GridLayout)

利用 NetBeans 開發視窗程式 NetBeans 開啟後,請在功能表列選取”File””New Project”, 選擇 “Java Application”後,按”Next>”鈕

利用 NetBeans 開發視窗程式 輸入 Project 名稱,例如 TestSwing 取消 “Create Main Class” 選取方塊 按”Finish”鈕

利用 NetBeans 開發視窗程式 選取 File  New File  Swing GUI Forms  JFrame Forms  輸入類別名稱

設定版面以及容器 在視窗的中央按下滑鼠右鍵,並從 “Set Layout” 中選取 “Border Layout”。 再來,把必要的 JPanel、JScrollPane 加上視窗 建議先放非中央的區塊

放完 JScrollPane 和 JTextArea 後的視窗如下。

從 NetBeans 右下角的區塊(如下圖),可以看出欄寬(columns)是 20,列高(rows)是 5 這個畫面也可以說明為什麼建議對每一個資料成員設計 accessors 和 mutators 的原因。

我們需要從右上角選取”Panel”圖示並將其拖曳到文字方塊的左邊,並設定版面為GridLayout(在左邊視窗按滑鼠右鍵,並從”Set Layout”中選取”Grid Layout”) 由於我們希望在上半部放性別的選擇鈕,下半部放收入的選擇鈕,因此Grid Layout要設定成兩列一欄,而設定的方式是在NetBeans右下角的區塊的”Columns”設定為 1,”Rows”設定為 2

GUI 元件的關係呈現在 NetBeans左下角的區塊

設定完 jPanel3和 jPanel4之後,我們需要在 jPanel2 上加入兩個選擇鈕。然後從右上角把”Radio Button”圖示拖曳到適當位置,拖曳後的畫面如下

NetBeans 右下角區塊的 ”Text” 改成女性

把另一個選擇鈕的文字標籤也改成”男性”

右上角拖曳 ”Button Group” 圖示到視窗的區塊,在左下角的區塊就會產生一個名為buttonGroup1 的元件

jRadioButton1以及jRadioButton2的物件加到buttonGroup1的群組內

產生四個收入區間的選擇鈕,並將這四個選擇鈕加到buttonGroup2 中

我們從 NetBeans 左下角區塊點選 jPanel3,然後到 NetBeans 右下角區塊中設定 ”border” 的值;我們可以從選單中選擇 ”Titled Border”,並在”Title”設定”性別”

對jPanel4作類似的動作

我們在 “女性” 選擇鈕上按滑鼠右鍵,在 “Events” 內選取 “Item””itemStateChanged”

完成事件處理方法

從功能表列選取”Run””Run Main Project”來執行,畫面如下:

練習題 請利用 NetBeans 完成之前的所有視窗程式。 請仔細檢查 NetBeans 所產生的程式碼,並說明 NetBeans 的事件處理,採用的是第幾種方式? 以 itemStateChanged 為例,剛剛的範例中同樣的動作我們要重複多次,麻煩吧。怎麼解? 請比較事件處理方法的設計,我們提供的比較好?還是 NetBeans 提供的比較好?

練習題 請利用 NetBeans 完成 HourlyWorker/SalaryWorker 的 GUI 範例。步驟: 利用 new file,依序產生 Worker、HourlyWorker 和 SalaryWorker (先利用 NetBeans 產生架構後,再將之前的程式碼,複製貼上即可) 利用 new Swing GUI Forms 完成介面設計以及事件處理程式。