5.1 用AWT生成圖形化用戶界面
抽象窗口工具包AWT (Abstract Window Toolkit) 是 API爲Java 程序提供的建立圖形用戶界面GUI (Graphics User Interface)工具集,AWT可用於Java的applet和applications中。它支持圖形用戶界面編程的功能包括: 用戶界面組件;事件處理模型;圖形和圖像工具,包括形狀、顏色和字體類;佈局管理器,可以進行靈活的窗口布局而與特定窗口的尺寸和屏幕分辨率無關;數據傳送類,可以通過本地平臺的剪貼板來進行剪切和粘貼。
5.1.1 java.awt包
java.awt包中提供了GUI設計所使用的類和接口,可從圖5.1中看到主要類之間的關係。
java.awt包提供了基本的java程序的GUI設計工具。主要包括下述三個概念:
組件--Component
容器--Container
佈局管理器--LayoutManager
5.1.2 組件和容器
Java的圖形用戶界面的最基本組成部分是組件(Component),組件是一個可以以圖形化的方式顯示在屏幕上並能與用戶進行交互的對象,例如一個按鈕,一個標籤等。組件不能獨立地顯示出來,必須將組件放在一定的容器中纔可以顯示出來。
類java.awt.Component是許多組件類的父類,Component類中封裝了組件通用的方法和屬性,如圖形的組件對象、大小、顯示位置、前景色和背景色、邊界、可見性等,因此許多組件類也就繼承了Component類的成員方法和成員變量,相應的成員方法包括: 在程序中安排組件的位置和大小時,應該注意以下兩點: 2.如果用戶確實需要親自設置組件大小或位置,則應取消該容器的佈局管理器,方法爲: 5.1.3 常用容器 1.Frame
getComponentAt(int x, int y)
getFont()
getForeground()
getName()
getSize()
paint(Graphics g)
repaint()
update()
setVisible(boolean b)
setSize(Dimension d)
setName(String name)等
容器(Container)也是一個類,實際上是Component的子類,因此容器本身也是一個組件,具有組件的所有性質,但是它的主要功能是容納其它組件和容器。
佈局管理器(LayoutManager):每個容器都有一個佈局管理器,當容器需要對某個組件進行定位或判斷其大小尺寸時,就會調用其對應的佈局管理器。
爲了使我們生成的圖形用戶界面具有良好的平臺無關性,Java語言中,提供了佈局管理器這個工具來管理組件在容器中的佈局,而不使用直接設置組件位置和大小的方式。
1.容器中的佈局管理器負責各個組件的大小和位置,因此用戶無法在這種情況下設置組件的這些屬性。如果試圖使用Java 語言提供的setLocation(),setSize(),setBounds() 等方法,則都會被佈局管理器覆蓋。
setLayout(null);
容器java.awt.Container是Component的子類,一個容器可以容納多個組件,並使它們成爲一個整體。容器可以簡化圖形化界面的設計,以整體結構來佈置界面。所有的容器都可以通過add()方法向容器中添加組件。
有三種類型的容器:Window、Panel、ScrollPane,常用的有Panel, Frame, Applet。
以下是容器的例子:
例5.1
import java.awt.*;
public class MyFrame extends Frame{
public static void main(String args[ ]){
MyFrame fr = new MyFrame("Hello Out There!");
//構造方法
fr.setSize(200,200);
//設置Frame的大小,缺省爲(0,0)
fr.setBackground(Color.red);
//設置Frame的背景,缺省爲紅色
fr.setVisible(true);
//設置Frame爲可見,缺省爲不可見
}
public MyFrame (String str){
super(str); //調用父類的構造方法
}
}
一般我們要生成一個窗口,通常是用Window的子類Frame來進行實例化,而不是直接用到Window類。Frame的外觀就像我們平常在windows系統下見到的窗口,有標題、邊框、菜單、大小等等。每個Frame的對象實例化以後,都是沒有大小和不可見的,因此必須調用setSize( )來設置大小,調用setVisible(true)來設置該窗口爲可見的。
另外,AWT在實際的運行過程中是調用所在平臺的圖形系統,因此同樣一段AWT程序在不同的操作系統平臺下運行所看到的圖形系統是不一樣的。例如在windows下運行,則顯示的窗口是windows風格的窗口;而在UNIX下運行時,則顯示的是UNIX風格的窗口。
2. Panel
例5.2
import java.awt.*;
public class FrameWithPanel extends Frame{
public FrameWithPanel(String str){
super(str);
}
public static void main(String args[]){
FrameWithPanel fr = new FrameWithPanel("Frame with Panel");
Panel pan=new Panel();
fr.setSize(200,200);
fr.setBackground(Color.red);
//框架fr的背景顏色設置爲紅色
fr.setLayout(null);
//取消佈局管理器
pan.setSize(100,100);
pan.setBackground(Color.yellow);
//設置面板pan的背景顏色爲黃色
fr.add(pan); //用add方法把面板pan添加到框架fr中
fr.setVisible(true);
}
}
查看運行結果
一般我們要生成一個窗口,通常是用Window的子類Frame來進行實例化,而不是直接用到Window類。Frame的外觀就像我們平常在windows系統下見到的窗口,有標題、邊框、菜單、大小等等。每個Frame的對象實例化以後,都是沒有大小和不可見的,因此必須調用setSize( )來設置大小,調用setVisible(true)來設置該窗口爲可見的。
另外,AWT在實際的運行過程中是調用所在平臺的圖形系統,因此同樣一段AWT程序在不同的操作系統平臺下運行所看到的圖形系統是不一樣的。例如在windows下運行,則顯示的窗口是windows風格的窗口;而在UNIX下運行時,則顯示的是UNIX風格的窗口。
5.1.4 LayoutManager 佈局管理器(1)
java爲了實現跨平臺的特性並且獲得動態的佈局效果,java將容器內的所有組件安排給一個"佈局管理器"負責管理,如:排列順序,組件的大小、位置,當窗口移動或調整大小後組件如何變化等功能授權給對應的容器佈局管理器來管理,不同的佈局管理器使用不同算法和策略,容器可以通過選擇不同的佈局管理器來決定佈局。
佈局管理器主要包括:FlowLayout,BorderLayout,GridLayout,CardLayout,GridBagLayout public void go(){
例5.3
import java.awt.*;
public class ExGui{
private Frame f;
private Button b1;
private Button b2;
public static void main(String args[]){
ExGui that = new ExGui();
that.go();
}
f = new Frame("GUI example");
f.setLayout(new FlowLayout());
//設置佈局管理器爲FlowLayout
b1 = new Button("Press Me");
//按鈕上顯示字符"Press Me"
b2 = new Button("Don't Press Me");
f.add(b1);
f.add(b2);
f.pack();
//緊湊排列,其作用相當於setSize(),即讓窗口
儘量小,小到剛剛能夠包容住b1、b2兩個按鈕
f.setVisible(true);
}
}
查看運行結果
1. FlowLayout
FlowLayout 是Panel,Applet的缺省佈局管理器。其組件的放置規律是從上到下、從左到右進行放置,如果容器足夠寬,第一個組件先添加到容器中第一行的最左邊,後續的組件依次添加到上一個組件的右邊,如果當前行已放置不下該組件,則放置到下一行的最左邊。
構造方法主要下面幾種:
FlowLayout(FlowLayout.RIGHT,20,40);
/*第一個參數表示組件的對齊方式,指組件在這一行中的位置是居中對齊、居右對齊還是居左對齊,第二個參數是組件之間的橫向間隔,第三個參數是組件之間的縱向間隔,單位是象素。*/
FlowLayout(FlowLayout.LEFT);
//居左對齊,橫向間隔和縱向間隔都是缺省值5個象素
FlowLayout();
//缺省的對齊方式居中對齊,橫向間隔和縱向間隔都是缺省值5個象素
例5.4
import java.awt.*;
public class myButtons{
public static void main(String args[])
{
Frame f = new Frame();
f.setLayout(new FlowLayout());
Button button1 = new Button("Ok");
Button button2 = new Button("Open");
Button button3 = new Button("Close");
f.add(button1);
f.add(button2);
f.add(button3);
f.setSize(300,100);
f.setVisible(true);
}
當容器的大小發生變化時,用FlowLayout管理的組件會發生變化,其變化規律是:組件的大小不變,但是相對位置會發生變化。例如上圖中有三個按鈕都處於同一行,但是如果把該窗口變窄,窄到剛好能夠放下一個按鈕,則第二個按鈕將折到第二行,第三個按鈕將折到第三行。按鈕"Open"本來在按鈕"OK"的右邊,但是現在跑到了下面,所以說"組件的大小不變,但是相對位置會發生變化"。
2. BorderLayout
BorderLayout 是Window,Frame和Dialog的缺省佈局管理器。BorderLayout佈局管理器把容器分成5個區域:North,South,East,West和Center,每個區域只能放置一個組件。各個區域的位置及大小如下圖所示:
例5.5
import java.awt.*;
public class buttonDir{
public static void main(String args[]){
Frame f = new Frame("BorderLayout");
f.setLayout(new BorderLayout());
f.add("North", new Button("North"));
//第一個參數表示把按鈕添加到容器的North區域
f.add("South", new Button("South"));
//第一個參數表示把按鈕添加到容器的South區域
f.add("East", new Button("East"));
//第一個參數表示把按鈕添加到容器的East區域
f.add("West", new Button("West"));
//第一個參數表示把按鈕添加到容器的West區域
f.add("Center", new Button("Center"));
//第一個參數表示把按鈕添加到容器的Center區域
f.setSize(200,200);
f.setVisible(true);
}
}
查看運行結果
在使用BorderLayout的時候,如果容器的大小發生變化,其變化規律爲:組件的相對位置不變,大小發生變化。例如容器變高了,則North、South區域不變,West、Center、East區域變高;如果容器變寬了,West、East區域不變,North、Center、South區域變寬。不一定所有的區域都有組件,如果四周的區域(West、East、North、South區域)沒有組件,則由Center區域去補充,但是如果Center區域沒有組件,則保持空白,其效果如下幾幅圖所示:
North區域缺少組件
North和Center區域缺少組件
3. GridLayout
使容器中各個組件呈網格狀佈局,平均佔據容器的空間。
例5.6
import java.awt.*;
public class ButtonGrid {
public static void main(String args[]) {
Frame f = new Frame("GridLayout");
f.setLayout(new GridLayout(3,2));
//容器平均分成3行2列共6格
f.add(new Button("1")); //添加到第一行的第一格
f.add(new Button("2")); //添加到第一行的下一格
f.add(new Button("3")); //添加到第二行的第一格
f.add(new Button("4")); //添加到第二行的下一格
f.add(new Button("5")); //添加到第三行的第一格
f.add(new Button("6")); //添加到第三行的下一格
f.setSize(200,200);
f.setVisible(true);
}
}
5.1.4 LayoutManager 佈局管理器(2)
4. CardLayout public static void main(String args[]) public void go(){ 5.2 AWT事件處理模型 例如,如果用戶用鼠標單擊了按鈕對象button,則該按鈕button就是事件源,而java運行時系統會生成ActionEvent類的對象actionE,該對象中描述了該單擊事件發生時的一些信息,然後,事件處理者對象將接收由java運行時系統傳遞過來的事件對象actionE並進行相應的處理。 使用授權處理模型進行事件處理的一般方法歸納如下:
CardLayout佈局管理器能夠幫助用戶處理兩個以至更多的成員共享同一顯示空間,它把容器分成許多層,每層的顯示空間佔據整個容器的大小,但是每層只允許放置一個組件,當然每層都可以利用Panel來實現複雜的用戶界面。牌佈局管理器(CardLayout)就象一副疊得整整齊齊的撲克牌一樣,有54張牌,但是你只能看見最上面的一張牌,每一張牌就相當於牌佈局管理器中的每一層。
例5.7
import java.awt.*;
import java.awt.event.*; //事件處理機制,下一節的內容
public class ThreePages implements MousListener {
CardLayout layout=new CardLayout(); //實例化一個牌佈局管理器對象
Frame f=new Frame("CardLayout");
Button page1Button;
Label page2Label; //Label是標籤,實際上是一行字符串
TextArea page3Text; //多行多列的文本區域
Button page3Top;
Button page3Bottom;
{ new ThreePages().go(); }
Public void go()
{ f.setLayout(layout); //設置爲牌佈局管理器layout
f.add(page1Button=new Button("Button page"),"page1Button"); /*第二個參數"page1Button"表示的是你對這層牌所取的名字*/
page1Button.addMouseListener(this); //註冊監聽器
f.add(page2Label=new Label("Label page"),"page2Label");
page2Label.addMouseLisener(this); //註冊監聽器
Panel panel=new Panel();
panel.setLayout(new BorderLayout());
panel.add(page3Text=new TextArea("Composite page"),"Center");
page3Text.addMouseListener(this);
panel.add(page3Top=new Button("Top button") , "North");
page3Top.addMouseListener(this);
panel.add(page3Bottom=new Button("Bottom button") ,"South");
page3Bottom.addMouseListener(this);
f.add(panel,"panel");
f.setSize(200,200);
f.setVisible(true);
}
……
}
5.容器的嵌套
在複雜的圖形用戶界面設計中,爲了使佈局更加易於管理,具有簡潔的整體風格,一個包含了多個組件的容器本身也可以作爲一個組件加到另一個容器中去,容器中再添加容器,這樣就形成了容器的嵌套。下面是一個容器嵌套的例子。
例5.8
import java.awt.*;
public class ExGui3{
private Frame f;
private Panel p;
private Button bw,bc;
private Button bfile,bhelp;
public static void main(String args[])
{
ExGui3 gui = new ExGui3();
gui.go();
}
f = new Frame("GUI example 3");
bw=new Button("West");
bc=new Button("Work space region");
f.add(bw,"West");
f.add(bc,"Center");
p = new Panel();
f.add(p,"North");
bfile= new Button("File");
bhelp= new Button("Help");
p.add(bfile);
p.add(bhelp);
f.pack();
f.setVisible(true);
}
}
查看運行結果
小 結:
1.Frame是一個頂級窗口。Frame的缺省佈局管理器爲BorderLayout。
2.Panel 無法單獨顯示,必須添加到某個容器中。 Panel 的缺省佈局管理器爲FlowLayout。
3.當把Panel 作爲一個組件添加到某個容器中後,該Panel 仍然可以有自己的佈局管理器。因此,可以利用Panel 使得BorderLayout 中某個區域顯示多個組件,達到設計複雜用戶界面的目的 。
4.如果採用無佈局管理器 setLayout(null),則必須使用setLocation(),setSize(),setBounds()等方法手工設置組件的大小和位置,此方法會導致平臺相關,不鼓勵使用。
上一節中的主要內容是如何放置各種組件,使圖形界面更加豐富多彩,但是還不能響應用戶的任何操作,要能夠讓圖形界面接收用戶的操作,就必須給各個組件加上事件處理機制。在事件處理的過程中,主要涉及三類對象:
◇ Event-事件,用戶對界面操作在java語言上的描述,以類的形式出現,例如鍵盤操作對應的事件類是KeyEvent。
◇ Event Source-事件源,事件發生的場所,通常就是各個組件,例如按鈕Button。
◇ Event handler-事件處理者,接收事件對象並對其進行處理的對象
由於同一個事件源上可能發生多種事件,因此java採取了授權處理機制(Delegation Model),事件源可以把在其自身所有可能發生的事件分別授權給不同的事件處理者來處理。比如在Canvas對象上既可能發生鼠標事件,也可能發生鍵盤事件,該Canvas對象就可以授權給事件處理者一來處理鼠標事件,同時授權給事件處理者二來處理鍵盤事件。有時也將事件處理者稱爲監聽器,主要原因也在於監聽器時刻監聽着事件源上所有發生的事件類型,一旦該事件類型與自己所負責處理的事件類型一致,就馬上進行處理。授權模型把事件的處理委託給外部的處理實體進行處理,實現了將事件源和監聽器分開的機制。事件處理者(監聽器)通常是一個類,該類如果要能夠處理某種類型的事件,就必須實現與該事件類型相對的接口。例如例5.9中類ButtonHandler之所以能夠處理ActionEvent事件,原因在於它實現了與ActionEvent事件對應的接口ActionListener。每個事件類都有一個與之相對應的接口。
將事件源對象和事件處理器(事件監聽器)分開。如圖5.2所示
打個不太恰當的比喻,比如說有一位李先生,李先生可能會發生很多法律糾紛,可能是民事法律糾紛,也可能是刑事法律糾紛,那麼李先生可以請律師,他可以授權王律師負責幫他打民事法律的官司,同時也可以授權張律師幫他打刑事法律的官司。這個請律師的過程從李先生的角度來看,就是授權的過程,而從王律師和張律師的角度來看,一旦被授權,他們就得時刻對李先生負責,"監聽"着李先生,一旦發生民事糾紛了,王律師就要馬上去處理,而一旦發生刑事糾紛了,張律師就要馬上進行處理。此時此刻,李先生就是事件源,王律師是一個事件處理者,張律師是另外一個事件處理者,民事糾紛和刑事糾紛就是不同類型的事件。
例5.9
import java.awt.*;
import java.awt.event.*;
public class TestButton {
public static void main(String args[])
{
Frame f = new Frame("Test");
Button b = new Button("Press Me!");
b.addActionListener(new ButtonHandler()); /*註冊監聽器進行授權,該方法的參數是事件處理者對象,要處理的事件類型可以從方法名中看出,例如本方法要授權處理的是ActionEvent,因爲方法名是addActionListener。*/
f.setLayout(new FlowLayout()); //設置佈局管理器
f.add(b);
f.setSize(200,100);
f.setVisible(true);
}
}
class ButtonHandler implements ActionListener {
//實現接口ActionListener才能做事件ActionEvent的處理者
public void actionPerformed(ActionEvent e)
//系統產生的ActionEvent事件對象被當作參數傳遞給該方法
{
System.out.println("Action occurred");
//本接口只有一個方法,因此事件發生時,系統會自動調用本方法,需要做的操作就把代碼寫在則個方法裏。
}
}
1.對於某種類型的事件XXXEvent, 要想接收並處理這類事件,必須定義相應的事件監聽器類,該類需要實現與該事件相對應的接口XXXListener;
2.事件源實例化以後,必須進行授權,註冊該類事件的監聽器,使用addXXXListener(XXXListener ) 方法來註冊監聽器。