Java图形化界面编程之——AWT
目錄
1、AWT簡介
2、AWT繼承體系
3、Container容器
3.1、Container繼承體系
3.2、常用API
3.2.1、Component的常用方法
3.2.2、Container的常用方法
3.3、容器演示
3.3.1、Window
3.3.2、Panel
3.3.3、ScrollPane
4、LayoutManager布局管理器
4.1、FlowLayout(流式布局)
4.2、BorderLayout(邊框布局)
4.3、GridLayout(網格布局)
4.4、GridBagLayout(網格包布局)
4.5、CardLayout(卡片布局)
4.6、BoxLayout(盒子布局)
4.6.1、Box容器
5、AWT中常用組件
5.1、基本組件
5.2、Dialog(對話框)
5.2.1、FileDialog(文件對話框)
6、事件處理
6.1、GUI事件處理機制
6.2、GUI中常見事件和事件監聽器
6.2.1、事件
6.2.2、事件監聽器
6.2.3、案例
7、菜單組件
8、繪圖
8.1、組件繪圖原理
8.2、Graphics對象的使用
8.3、處理位圖
8.4、ImageIO的使用
1、AWT簡介
AWT的全稱是抽象窗口工具集(Abstract Window Toolkit),它是sun公司最早提供的GUI庫,這個GUI類庫希望可以在所有平臺下都能運行,它為Java應用程序提供了基本的圖形組件,但這個GUI庫的功能比較有限,所以后來sun公司又提供了Swing庫。通過使用AWT和Swing提供的圖形化界面組件庫,java的圖形化界面編程非常簡單,程序只需要依次創建所需的圖形組件,并以合適的方式將這些組件組織在一起,就可以開發出非常美觀的用戶界面。
AWT是窗口框架,它從不同平臺的窗口系統中抽取出共同組件,當程序運行時,將這些組件的創建和動作委托給程序所在的運行平臺。簡而言之,當使用AWT編寫圖形界面應用時,程序僅制定了界面組件的位置和行為,并為提供真正的實現,JVM調用操作系統本地的圖形界面來創建和平臺一致的對等體。
使用AWT創建的圖形界面應用和所在的運行平臺有相同的界面風格,比如在Windows操作系統上,它就表現出Windows風格;在UNIX操作系統上,它就表現出UNIX風格。Sun希望采用這種方式來實現“Write Once,Run Anywhere”的目標。
2、AWT繼承體系
AWT繼承體系所有和AWT編程相關的類都放在java.awt包以及它的子包中。AWT編程中有兩個基類:Component和MenuComponent:
- Component:代表一個能以圖形化方式顯式出來,并可與用戶交互的對象。
- MenuComponent:代表圖形界面的菜單組件,包括MenuBar(菜單條)、MenuItem(菜單項)等子類。
其中Container是一種特殊的Component,它代表一種容器,可以盛裝普通的Component。
AWT中還有一個非常重要的接口叫LayoutManager,如果一個容器中有多個組件,那么容器就需要使用LayoutManager來管理這些組件的布局方式。
接口LayoutManager:
- GridLayout
- FlowLayout
- ...
3、Container容器
3.1、Container繼承體系
Container容器:
??????? 1、Window窗口容器:
??????????????? Frame:創建窗口
??????????????? Dialog:創建對話框
??????? 2、Panel內嵌容器:
??????????????? Applet
??????? 3、ScrollPane含有滾動條的容器
- Window是可以獨立存在的頂級窗口,默認使用BorderLayout管理其內部組件布局;
- Panel可以容納其他組件,但不能獨立存在,它必須內嵌其他容器中使用,默認使用FlowLayout管理其內部組件布局;
- ScrollPane是一個帶滾動條的容器,它也不能獨立存在,默認使用BorderLayout管理其內部組件布局。
3.2、常用API
3.2.1、Component的常用方法
Component作為基類,提供了如下常用的方法來設置組件的大小、位置、可見性等。
| setLocation(int x, int y) | 設置i組件的位置 |
| setSize(int width, int height) | 設置組件的大小 |
| setBounds(int x, int y, int width, int height) | 同時設置組件的位置、大小 |
| setVisible(Boolean b) | 設置該組件的可見性 |
3.2.2、Container的常用方法
| Component add(Component comp) | 向容器中添加其他組件(該組件既可以是普通組件,也可以是容器),并返回被添加的組件 |
| Component getComponentAt(int x, int y) | 返回指定點的組件 |
| int getComponentCount() | 返回該容器內組件的數量 |
| Component[] getComponent() | 返回該容器內的所有組件 |
3.3、容器演示
3.3.1、Window
package Package1;import java.awt.*;public class FrameDemo {public static void main(String[] args) {//1、創建窗口對象Frame frame = new Frame("這是一個窗口容器Frame");//2、設置窗口的位置和大小frame.setLocation(100,100); //單位是像素frame.setSize(500,300); //單位是像素//3、設置窗口對象可見frame.setVisible(true);} }3.3.2、Panel
package Package1;import java.awt.*;public class PanelDemo {public static void main(String[] args) {//1、創建一個Window對象,因為Panel以及其它的容器,都不能獨立存在,必須依附于Window存在Frame frame = new Frame("這是一個窗口容器Frame");//2、創建一個Panel對象Panel p = new Panel();//3、創建一個文本框和一個按鈕,并且把它們放入Panel容器中p.add(new TextField("這是一個測試文本"));p.add(new Button("這是一個測試按鈕"));//4、把Panel放入Window中frame.add(p);//5、設置Window的位置和大小frame.setBounds(400,200,500,300);//6、設置Window可見frame.setVisible(true);} }按鈕文本顯示亂碼解決辦法:依次點擊Run -> Edit Configurations -> 左邊選中要修改的項 -> 在右側找到并點擊Modify options -> 點擊Add VM options -> 在新出現的框中輸入 -Dfile.encoding=gbk -> 點擊OK 。
3.3.3、ScrollPane
package Package1;import java.awt.*;public class ScrollPaneDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//創建一個ScrollPane對象ScrollPane sp = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS);sp.add(new TextField("這是測試文本"));sp.add(new Button("這是測試按鈕"));//把ScrollPane添加到Frame中frame.add(sp);//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }程序明明向ScrollPane中添加了一個文本框和一個按鈕,但只能看到一個按鈕,卻看不到文本框,這是因為ScrollPane使用BorderLayout布局管理器的緣故,BorderLayout導致了該容器中只有一個組件被顯示出來。
4、LayoutManager布局管理器
如果要讓我們的程序在不同的操作系統下,都有相同的體驗,那么手動設置組件的位置和大小,無疑是一種災難,因為有太多的組件,需要分別設置不同操作系統下的大小和位置。為了解決這個問題,Java提供了LayoutManager布局管理器,可以根據運行平臺來自動調整組件大小,程序員不用再手動設置組件大小和位置了,只需要為容器選擇合適的布局管理器即可。
AWT中接口LayoutManager有五個實現類:GridLayout(網格布局)、FlowLayout(流式布局)、CardLayout(卡片布局)、GridBagLayout(網格包布局)和BorderLayout(邊框布局)。為了簡化開發,Swing引入了一個新的布局管理器BoxLayout。
4.1、FlowLayout(流式布局)
在FlowLayout布局管理器中,組件像水流一樣向某方向流動(排列),遇到障礙(邊界)就折回,中心開始排列。在默認情況下,FlowLayout布局管理器從左向右排列所有組件,遇到邊界就會折回下一行重新開始。
| FlowLayout() | 使用默認的對齊方式及默認的垂直間距、水平間距創建FlowLayout布局管理器 |
| FlowLayout(int align) | 使用指定的對齊方式及默認的垂直間距、水平間距創建FlowLayout布局管理器 |
| FlowLayout(int align, int hgap, int vgap) | 使用指定的對齊方式及指定的垂直間距、水平間距創建FlowLayout布局管理器 |
FlowLayout中組件的排列方向(從左向右、從右向左、從中間向兩邊等),該參數應該使用FlowLayout類的靜態常量:FlowLayout.LEFT、FlowLayout.CENTER、FlowLayout.RIGHT。默認是左對齊。
FlowLayout中組件中間距通過整數設置,單位是像素,默認是5個像素。
代碼演示:
package Package1;import java.awt.*;public class FlowLayoutDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//通過setLayout()方法設置容器的布局管理器frame.setLayout(new FlowLayout(FlowLayout.LEFT,20,20));//添加多個按鈕到frame中for (int i = 0; i < 10; i++) {frame.add(new Button("按鈕"+i));}//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }4.2、BorderLayout(邊框布局)
BorderLayout將容器分為EAST、SOUTH、WEST、CENTER五個區域,普通組件可以被放置在這5個區域的任意一個中。
?當改變使用BorderLayout的容器大小時,NORTH、SOUTH和CENTER區域水平調整,而EAST、WEST和CENTER區域垂直調整,使用BorderLayout有如下兩個注意點:
- 當向使用BorderLayout布局的容器中添加組件時,需要指定要添加到哪個區域中。如果沒有指定添加到哪個區域中,則默認添加到中間區域中;
- 如果向同一個區域中添加多個組件,后放入的組件會覆蓋先放入的組件。
| BorderLayout() | 使用默認的水平間距、垂直間距創建BorderLayout布局管理器 |
| BorderLayout(int hgap, int vgap) | 使用指定的水平間距、垂直間距創建BorderLayout布局管理器 |
代碼演示:
package Package1;import java.awt.*;public class BorderLayoutDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//通過setLayout()方法設置容器的布局管理器frame.setLayout(new BorderLayout(30,10));//往frame的指定區域添加組件frame.add(new Button("北側按鈕"),BorderLayout.NORTH);frame.add(new Button("南側按鈕"),BorderLayout.SOUTH);frame.add(new Button("東側按鈕"),BorderLayout.EAST);frame.add(new Button("西側按鈕"),BorderLayout.WEST);frame.add(new Button("中間按鈕"),BorderLayout.CENTER);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }4.3、GridLayout(網格布局)
GridLayout布局管理器將容器分割成縱橫線分隔的網格,每個網格所占的區域大小相同。當向使用GridLayout布局管理器的容器中添加組件時,默認從左到右、從上到下依次添加到每個網格中。與FlowLayout不同的是,放置在GridLayout布局管理器中的各組件的大小由組件所處的區域決定(每個組件將自動占滿整個區域)。
| GridLayout(int rows, int cols) | 采用指定的行數、列數,以及默認的橫向間距、縱向間距將容器分割成多個網格。 |
| GridLayout(int rows, int cols, int hgap, int vgap) | 采用指定的行數、列數,以及指定的橫向間距、縱向間距將容器分割成多個網格。 |
代碼演示:
package Package1;import java.awt.*;public class GridLayoutDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//通過setLayout()方法設置容器的布局管理器frame.setLayout(new BorderLayout());//創建一個Panel對象,里面存放一個TextField組件Panel p1 = new Panel();p1.add(new TextField(50));//把這個Panel添加到frame的北邊區域frame.add(p1,BorderLayout.NORTH);//創建一個Panel對象,并且設置它的布局管理器為GridLayoutPanel p2 = new Panel();p2.setLayout(new GridLayout(3,5,4,4));//往Panel中添加內容for (int i = 0; i < 10; i++) {p2.add(new Button(i+""));}p2.add(new Button("+"));p2.add(new Button("-"));p2.add(new Button("*"));p2.add(new Button("/"));p2.add(new Button("."));//把當前的Panel添加到frame中frame.add(p2,BorderLayout.CENTER);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(500,200,400,250);//設置Frame可見frame.setVisible(true);} }4.4、GridBagLayout(網格包布局)
GridBagLayout布局管理器的功能最強大,但也最復雜,與GirdLayout布局管理器不同的是,在GridBagLayout布局管理器中,一個組件可以跨越一個或多個網格,并且可以設置網格的大小互不相同,從而增加了布局的靈活性。當窗口的大小發生變化時,GridBagLayout布局管理器也可以準確地控制窗口各部分的拉伸。
由于在GirdBagLayout布局中,每個組件可以占用多個網格,此時,我們往容器中添加組件的時候,就需要具體地控制每個組件占用多少個網格,java提供的GridBagConstraints類,與特定的組件綁定,可以完成具體大小和跨越性的設置。
GirdBagConstraints API:
| static int | BOTH | 水平和垂直調整組件大小 |
| static int | CENTER | 將組件放在其顯示區域的中心 |
| static int | EAST | 將組件放在顯示區域的右側,垂直居中 |
| static int | REMAINDER | 指定此組件是其列或行中的最后一個組件 |
| int | gridheight | 指定組件顯示區域的列中的單元格數 |
| int | gridwidth | 指定組件顯示區域的行中單元格數 |
| int | gridx | 指定包含組件顯示區域前沿的單元格,其中一行中的第一個單元格具有gridx=0 |
| int | gridy | 指定組件顯示區域頂部的單元格,其中最頂層的單元格為gridy=0 |
| double | weightx | 指定如何分配額外的水平空間 |
| double | weighty | 指定如何分配額外的垂直空間 |
代碼演示:
package Package1;import java.awt.*;public class GirdBagLayoutDemo {public static void main(String[] args) {//創建Frame對象Frame frame = new Frame("這是一個窗口容器Frame");//創建GridBagLayout對象GridBagLayout gbLayout = new GridBagLayout();//把Frame對象的布局管理器設置為GridBagLayoutframe.setLayout(gbLayout);//創建GridBagConstraints對象GridBagConstraints gbCon = new GridBagConstraints();/*設置所有的GridBagConstraints對象的fill屬性為GridBagConstraints.BOTH。當有空白區域時,組件自動擴大占滿空白區域*/gbCon.fill = GridBagConstraints.BOTH;//設置GridBagConstraints對象的weightx設置為1,表示橫向擴展比例為1gbCon.weightx = 1;//把GridBagConstraints的weighty設置為1,表示縱向擴展比例為1gbCon.weighty = 1;//創建容量為10的Button數組并初始化Button[] btns = new Button[10];for (int i = 0; i < btns.length; i++) {btns[i] = new Button("按鈕"+i);}//往frame中添加數組中的第0,1,2個ButtonaddComponent(frame,btns[0],gbLayout,gbCon);addComponent(frame,btns[1],gbLayout,gbCon);addComponent(frame,btns[2],gbLayout,gbCon);//把GridBagConstraints的gridwidth設置為GridBagConstraints.REMAINDER,表明當前組件是橫向最后一個組件gbCon.gridwidth = GridBagConstraints.REMAINDER;//把Button數組中第3個按鈕添加到frame中addComponent(frame,btns[3],gbLayout,gbCon);//把Button數組中第4個按鈕添加到frame中addComponent(frame,btns[4],gbLayout,gbCon);//把GridBagConstraints的gridheight和gridwidth設置為2,表示縱向和橫向會占用2個網格gbCon.gridwidth = 2;gbCon.gridheight = 2;//把Button數組中第5個按鈕添加到frame中addComponent(frame,btns[5],gbLayout,gbCon);//把GridBagConstraints的gridheight和gridwidth設置為1,表示縱向會占用1個網格gbCon.gridwidth = 1;gbCon.gridheight = 1;//把Button數組中第6個按鈕添加到frame中addComponent(frame,btns[6],gbLayout,gbCon);//把GridBagConstraints的gridwidth設置為GridBagConstraints.REMAINDER,表明當前組件是橫向最后一個組件gbCon.gridwidth = GridBagConstraints.REMAINDER;//把Button數組中第7個按鈕添加到frame中addComponent(frame,btns[7],gbLayout,gbCon);//把GridBagConstraints的gridheight設置為1,表示縱向會占用1個網格gbCon.gridwidth = 1;gbCon.gridheight = 1;//把Button數組中第8,9個按鈕添加到frame中addComponent(frame,btns[8],gbLayout,gbCon);addComponent(frame,btns[9],gbLayout,gbCon);//設置frame為最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置frame可見frame.setVisible(true);}public static void addComponent(Container container, Component c, GridBagLayout gbLayout, GridBagConstraints gbCon) {gbLayout.setConstraints(c,gbCon);container.add(c);} }4.5、CardLayout(卡片布局)
CardLayout布局管理器以時間而非空間來管理它里面的組件,它將加入容器的所有組件看成一疊卡片(每個卡片其實就是一個組件),每次只有最上面的哪個Component才可見。
| CardLayout() | 創建默認的CardLayout布局管理器 |
| CardLayout(int hgap, int vgap) | 通過指定卡片與容器左右邊界的間距(hgap)、上下邊界的間距(vgap)的間距來創建CardLayout布局管理器 |
| first(Container target) | 顯示target容器中的第一張卡片 |
| last(Container target) | 顯示target容器中的最后一張卡片 |
| previous(Container target) | 顯示target容器中的前一張卡片 |
| next(Container target) | 顯示target容器中的后一張卡片 |
| show(Container target, String name) | 顯示target容器中指定名字的卡片 |
代碼演示:
package Package1;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;public class CardLayoutDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//創建一個Panel,用來存儲多張卡片Panel p1 = new Panel();//創建CardLayout對象,并且把該對象設置給p1CardLayout cLayout = new CardLayout();p1.setLayout(cLayout);//往p1中存儲多個組件String[] names = {"第一張", "第二張", "第三張", "第四張", "第五張", "第六張"};for (int i = 0; i < names.length; i++) {//每次添加都是作為最后一個組件添加到末尾p1.add(names[i], new Button(names[i])); //添加時指定組件的名字}//把p1放到frame的中間區域frame.add(p1, BorderLayout.CENTER);//創建另外一個Panel,用來存放5個按鈕組件Panel p2 = new Panel();Button btn1 = new Button("上一張");Button btn2 = new Button("下一張");Button btn3 = new Button("第一張");Button btn4 = new Button("最后一張");Button btn5 = new Button("第三張");//創建一個事件監聽器,監聽按鈕的點擊動作ActionListener listener = new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String actionCommand = e.getActionCommand(); //這個字符串其實就是按鈕上的文字switch (actionCommand) {case "上一張":cLayout.previous(p1);break;case "下一張":cLayout.next(p1);break;case "第一張":cLayout.first(p1);break;case "最后一張":cLayout.last(p1);break;case "第三張":cLayout.show(p1, "第三張");break;}}};//把當前事件監聽器和多個按鈕綁定到一起btn1.addActionListener(listener);btn2.addActionListener(listener);btn3.addActionListener(listener);btn4.addActionListener(listener);btn5.addActionListener(listener);//把按鈕添加到容器p2中p2.add(btn1);p2.add(btn2);p2.add(btn3);p2.add(btn4);p2.add(btn5);//把p2放到frame的南邊區域frame.add(p2, BorderLayout.SOUTH);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400, 200, 500, 300);//設置Frame可見frame.setVisible(true);} }4.6、BoxLayout(盒子布局)
為了簡化開發,Swing引入了一個新的布局管理器BoxLayout。BoxLayout可以在垂直和水平兩個方向上擺放GUI組件,BoxLayout提供了如下一個簡單的構造器:
| BoxLayout(Container target, int axis) | 指定創建基于target容器的BoxLayout布局管理器,該布局管理器里的組件按axis方向排列。其中axis有BoxLayout.X_AXIS(橫向)和BoxLayout.Y_AXIS(縱向)兩個方向。 |
代碼演示:
package Package1;import javax.swing.*; import java.awt.*;public class BoxLayoutDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//基于frame容器創建一個BoxLayout對象,并且該對象存放組件是垂直存放BoxLayout boxLayout = new BoxLayout(frame,BoxLayout.Y_AXIS);//把BoxLayout設置給frameframe.setLayout(boxLayout);//往frame中添加兩個Button組件frame.add(new Button("按鈕1"));frame.add(new Button("按鈕2"));//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }4.6.1、Box容器
為了更方便地使用BoxLayout,在java.swing包中,提供了一個新的容器Box,該容器地默認布局管理器就是BoxLayout,大多數情況下,使用Box容器去容納多個GUI組件,然后再把Box容器作為一個組件,添加到其它地容器中,從而形成整體窗口布局。
| static Box createHorizontalBox() | 創建一個水平排列組件的Box容器 |
| static Box createVerticalBox() | 創建一個垂直排列組件的Box容器 |
代碼演示:
package Package1;import javax.swing.*; import java.awt.*;public class BoxLayoutDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//創建一個水平排列組件的Box容器Box hBox = Box.createHorizontalBox();//往當前容器中添加兩個按鈕hBox.add(new Button("水平按鈕1"));hBox.add(new Button("水平按鈕2"));//創建一個垂直排列組件的Box容器Box vBox = Box.createVerticalBox();//往當前容器中添加兩個按鈕vBox.add(new Button("垂直按鈕1"));vBox.add(new Button("垂直按鈕2"));//把兩個Box添加到Frame中展示frame.add(hBox,BorderLayout.NORTH);frame.add(vBox,BorderLayout.CENTER);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }通過上面的演示,我們會發現,被它管理的容器中的組件之間是沒有間隔的,是不是特別的美觀,但之前學習的幾種布局,組件之間都會有一些間距,那使用BoxLayout如何給組件設置間距呢?
其實很簡單,我們只需要在原有的組件需要間隔的地方,添加間隔即可,而每個間隔可以是一個組件,只不過該組件沒有內容,僅僅起到一種分隔的作用。
Box類中,提供了幾個方便的靜態方法來生成這些間隔組件:
| static Component createHorizontalGlue() | 創建一條水平Glue(可在兩個方向上同時拉伸的間距)。 |
| static Component createVerticalGlue() | 創建一條垂直Glue(可在兩個方向上同時拉伸的間距)。 |
| static Component createHorizontalStrut(int width) | 創建一條指定寬度(寬度固定,不能拉伸)的水平Strut(可在垂直方向上拉伸的間距)。 |
| static Component createVerticalStrut(int height) | 創建一條只當高度(高度固定,不能拉伸)的垂直Strut(可在水平方向上拉伸的間距)。 |
代碼演示:
package Package1;import javax.swing.*; import java.awt.*;public class BoxLayoutDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//創建一個水平排列組件的Box容器Box hBox = Box.createHorizontalBox();//往當前容器中添加兩個按鈕hBox.add(new Button("水平按鈕1"));hBox.add(Box.createHorizontalGlue()); //該分隔在兩個方向上都可以拉伸hBox.add(new Button("水平按鈕2"));hBox.add(Box.createHorizontalStrut(20));hBox.add(new Button("水平按鈕3"));//創建一個垂直排列組件的Box容器Box vBox = Box.createVerticalBox();//往當前容器中添加兩個按鈕vBox.add(new Button("垂直按鈕1"));vBox.add(Box.createVerticalGlue()); //該分隔在兩個方向上都可以拉伸vBox.add(new Button("垂直按鈕2"));vBox.add(Box.createVerticalStrut(20));vBox.add(new Button("垂直按鈕3"));//把兩個Box添加到Frame中展示frame.add(hBox,BorderLayout.NORTH);frame.add(vBox,BorderLayout.CENTER);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }5、AWT中常用組件
5.1、基本組件
| Button | 按鈕 |
| Canvas | 用于繪圖的畫布 |
| Checkbox | 復選框組件(也可當作單選框組件使用) |
| CheckboxGroup | 用于將多個Checkbox組件合成一組,一組Checkbox組件將只有一個可以被選中,即全部變成單選框組件 |
| Choice | 下拉選擇框 |
| Frame | 窗口,在GUI程序里通過該類創建窗口 |
| Label | 標簽類,用于放置提示文本 |
| List | 列表框組件,可以添加多項條目。條目通過調用列表元素對象的toString()方法來顯示其信息 |
| Panel | 不能單獨存在的基本容器類,必須放到其它容器中 |
| Scrollbar | 滑動條組件。如果需要用戶輸入位于某個范圍的值,就可以使用滑動條組件。當創建一個滑動條時,必須指定它的方向、初始值、滑塊的大小、最小值和最大值。 |
| ScrollPane | 帶水平及垂直滾動條的容器組件 |
| TextArea | 多行文本域 |
| TextField | 單行文本框 |
這些AWT組件的用法比較簡單,可以查閱API文檔來獲取它們的各自構造方法、成員方法等詳細信息。
代碼演示:
package Package1;import javax.swing.*; import java.awt.*;public class BasicComponentDemo {//組裝界面Frame frame = new Frame("這是一個窗口容器Frame");TextArea ta = new TextArea(5,20); //文本域Choice colorChooser = new Choice(); //下拉選擇框CheckboxGroup cbg = new CheckboxGroup(); //一組CheckboxCheckbox male = new Checkbox("男",cbg,true);Checkbox female = new Checkbox("女",cbg,false);Checkbox isMarried = new Checkbox("是否已婚?"); //一個CheckboxTextField tf = new TextField(50); //單行文本框Button ok = new Button("確認"); //按鈕List colorList = new List(6,true); //支持多選的6行列表//組裝界面public void init() {//組裝選擇部分為cBoxBox cBox = Box.createHorizontalBox();colorChooser.add("紅色");colorChooser.add("綠色");colorChooser.add("藍色");cBox.add(colorChooser);cBox.add(male);cBox.add(female);cBox.add(isMarried);//組裝文本域和選擇部分為topleftBox topLeft = Box.createVerticalBox();topLeft.add(ta);topLeft.add(cBox);//組裝topleft和列表框為top,并放置在frame的中間區域Box top = Box.createHorizontalBox();top.add(topLeft);colorList.add("紅色");colorList.add("綠色");colorList.add("藍色");top.add(colorList);frame.add(top,BorderLayout.CENTER);//組裝底部為bBox,并放置在frame的南邊區域Box bBox = Box.createHorizontalBox();bBox.add(tf);bBox.add(ok);frame.add(bBox,BorderLayout.SOUTH);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);}public static void main(String[] args) {new BasicComponentDemo().init();} }5.2、Dialog(對話框)
Dialog是Window類的子類,是一個容器類,屬于特殊組件。Dialog是可以獨立存在的頂級窗口,因此用法與普通窗口的用法幾乎完全一樣,但是使用對話框需要注意下面兩點:
- 對話框通常依賴于其它窗口,就是通常需要有一個父窗口;
- 對話框有非模式(non-modal)和模式(modal)兩種。當某個模式對話框被打開后,該模式對話框總是位于它的父窗口之上,在模式對話框被關閉之前,父窗口無法獲得焦點。
| Dialog(Frame owner, String title, boolean modal) | 創建一個對話框對象: owner:當前對話框的父窗口; title:當前對話框的標題; modal:當前對話框是否是模式對話框。true / false |
代碼演示:
package Package1;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;public class DialogDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//創建兩個對話框Dialog對象,一個模式的,一個非模式的Dialog mDialog = new Dialog(frame,"模式對話框",true);Dialog dialog = new Dialog(frame,"非模式對話框",false);//通過setBounds()方法設置Dialog 的位置及大小mDialog.setBounds(200,100,500,300);dialog.setBounds(200,100,500,300);//創建兩個按鈕Button btn1 = new Button("打開模式對話框");Button btn2 = new Button("打開非模式對話框");//給這兩個按鈕添加點擊后的行為btn1.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {mDialog.setVisible(true);}});btn2.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {dialog.setVisible(true);}});//把按鈕添加到frame中frame.add(btn1,BorderLayout.NORTH);frame.add(btn2,BorderLayout.CENTER);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }5.2.1、FileDialog(文件對話框)
Dialog類還有一個子類FileDialog,它代表一個文件對話框,用于打開或者保存文件,需要注意的是FileDialog無法確定模態或者非模態,這是因為FIleDialog依賴于運行平臺的實現,如果運行平臺的文件對話框是模態的,那么FileDialog也是模態的;否則就是非模態的。
| FileDialog(Frame parent, String title, int mode) | 創建一個文件對話框: parent:指定父窗口; title:對話框標題; mode:文件對話框類型,如果指定為FileDialog.LOAD,用于打開文件;如果指定為FileDialog.SAVE,用于保存文件。 |
| String getDirectory() | 獲取被打開或保存文件的絕對路徑 |
| String getFile() | 獲取被打開或保存文件的文件名 |
代碼演示:
package Package1;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;public class FileDialogDemo {public static void main(String[] args) {Frame frame = new Frame("這是一個窗口容器Frame");//創建2個FileDialog對象FileDialog f1 = new FileDialog(frame,"選擇要打開的文件",FileDialog.LOAD);FileDialog f2 = new FileDialog(frame,"選擇要保存的路徑",FileDialog.SAVE);//創建兩個按鈕Button b1 = new Button("打開文件");Button b2 = new Button("保存文件");//給這兩個按鈕設置點擊后的行為:獲取打開或者保存的路徑文件名b1.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {f1.setVisible(true); //代碼會阻塞到這里//獲取選擇的路徑及文件String directory = f1.getDirectory();String file = f1.getFile();System.out.println("打開的文件路徑為:"+directory);System.out.println("打開的文件名稱為:"+file);}});b2.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {f2.setVisible(true); //代碼會阻塞到這里String directory = f2.getDirectory();String file = f2.getFile();System.out.println("保存的文件路徑為:"+directory);System.out.println("保存的文件名稱為:"+file);}});//把按鈕添加到frame中frame.add(b1,BorderLayout.NORTH);frame.add(b2,BorderLayout.CENTER);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }6、事件處理
前面介紹了如何放置各種組件,從而得到了豐富多彩的圖形界面,但這些界面還不能響應用戶的任何操作。因為在AWT編程中,所有用戶的操作,都必須要經過一套事件處理機制來完成,而Frame和組件本身沒有事件處理能力。
6.1、GUI事件處理機制
定義:當在某個組件上發生某個操作的時候,會自動地觸發一段代碼的執行。
在GUI事件處理機制中涉及到4個重要的概念需要理解:
使用步驟:
代碼演示:
package Package1;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;public class EventDemo {Frame frame = new Frame("這是一個窗口容器Frame");TextField tf = new TextField(30); //30列的單行文本框Button ok = new Button("確定");//組裝視圖public void init() {MyListener myListener = new MyListener(); //創建自定義監聽器對象ok.addActionListener(myListener); //注冊監聽//把tf和ok翻入frame中frame.add(tf,BorderLayout.NORTH);frame.add(ok);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);}//自定義事件監聽器private class MyListener implements ActionListener {@Overridepublic void actionPerformed(ActionEvent e) {tf.setText("Hello World");}}public static void main(String[] args) {new EventDemo().init();} }6.2、GUI中常見事件和事件監聽器
事件監聽器必須實現事件監聽器接口,AWT提供了大量的事件監聽器接口用于實現不同類型的事件監聽器,來監聽不同類型的事件。AWT中提供了豐富的事件類,用于封裝不同組件上所發生的特定操作,AWT的事件類都是AWTEvent類的子類,AWTEvent類是EventObject類的子類。
6.2.1、事件
AWT把事件分為了兩大類:
1、低級事件:這類事件是基于某個特定動作的事件。比如進入、點擊、拖放等動作的鼠標事件,再比如得到焦點和失去焦點等焦點事件。
| ComponentEvent | 組件事件,當組件尺寸發生變化、位置發生移動、顯示/隱藏狀態發生改變時觸發該事件。 |
| ContainerEvent | 容器事件,當容器里發生添加組件、刪除組件時觸發該事件。 |
| WindowEvent | 窗口事件,當窗口狀態發生改變(如打開、關閉、最大化、最小化)時觸發該事件。 |
| FocusEvent | 焦點事件,當組件得到焦點或失去焦點時觸發該事件。 |
| KeyEvent | 鍵盤事件,當案件被按下、松開、單機時觸發該事件。 |
| MouseEvent | 鼠標事件,當進行單機、按下、松開、移動鼠標等動作時觸發該事件。 |
| PaintEvent | 組件繪制事件,該事件是一個特殊的事件類型,當GUI組件調用update/paint方法來呈現自身時觸發該事件,該事件并非專用于事件處理模型。 |
2、高級事件:這類事件并不會基于某個特定動作,而是根據功能含義定義的事件。
| ActionEvent | 動作事件,當按鈕、菜單項被單擊,在TextField中按Enter鍵時觸發。 |
| AdjustmentEvent | 調節事件,在滑動條上移動滑塊以調節數值時觸發該事件。 |
| ItemEvent | 選項事件,當用戶選中某項,或取消選中某項時觸發該事件。 |
| TextEvent | 文本事件,當文本框、文本域里的文本發生改變時觸發該事件。 |
6.2.2、事件監聽器
不同的事件需要使用不同的事件監聽器監聽,不同的監聽器需要實現不同的監聽器接口,當指定事件發生后,事件監聽器就會調用所包含的事件處理器(實例方法)來處理事件。
| ActionEvent | 激活組件 | ActionListener |
| ItemEvent | 選擇了某些項目 | ItemListener |
| MouseEvent | 鼠標移動 | MouseMotionListener |
| MouseEvent | 鼠標點擊等 | MouseListener |
| KeyEvent | 鍵盤輸入 | KeyListener |
| FocusEvent | 組件收到或失去焦點 | FocusListener |
| AdjustmentEvent | 移動了滾動條等組件 | AdjustmentListener |
| ComponentEvent | 對象移動縮放顯示隱藏等 | ComponentListener |
| WindowEvent | 窗口收到窗口級事件 | WindowListener |
| ContainerEvent | 容器中增加或刪除了組件 | ContainerListener |
| TextEvent | 文本字段或文本區發生改變 | TextListener |
6.2.3、案例
案例1:
package Package1;import javax.swing.*; import java.awt.*; import java.awt.event.*;public class ListenerDemo {public static void main(String[] args) {Frame frame = new Frame();//創建組件TextField tf = new TextField(30);Choice names = new Choice();names.add("張三");names.add("李四");names.add("王五");//給文本域添加TextListener,監聽內容的變化tf.addTextListener(new TextListener() {@Overridepublic void textValueChanged(TextEvent e) {String text = tf.getText();System.out.println("當前文本框的內容為:"+text);}});//給下拉選擇框添加ItemListener,監聽條目選項的變化names.addItemListener(new ItemListener() {@Overridepublic void itemStateChanged(ItemEvent e) {Object item = e.getItem();System.out.println("當前選中的條目為:"+item);}});//給frame注冊ContainerListener監聽器,監聽容器中組件的添加frame.addContainerListener(new ContainerListener() {@Overridepublic void componentAdded(ContainerEvent e) {Component child = e.getChild();System.out.println("frame中添加了:"+child);}@Overridepublic void componentRemoved(ContainerEvent e) {}});//添加到frame中Box hBox = Box.createHorizontalBox();hBox.add(names);hBox.add(tf);frame.add(hBox);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }案例2:給Frame設置WindowListener,監聽用戶點擊x的動作,如果用戶點擊x,則關閉當前窗口。
package Package1;import java.awt.*; import java.awt.event.*;public class ListenerDemo {public static void main(String[] args) {Frame frame = new Frame();//設置WindowListener,監聽用戶點擊x的動作,如果點擊x,則關閉窗口frame.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {//停止當前程序System.exit(0); //退出JVM}});//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);} }7、菜單組件
在實際開發中,除了主界面,還有一類比較重要的內容就是菜單相關組件,可以通過菜單相關組件很方便地使用特定的功能。在AWT中,菜單相關組件的使用和之前學習的組件是一模一樣的,只需要把菜單條、菜單、菜單項組合到一起,按照一定的布局,放入到容器中即可。
下表給出常用的菜單相關組件:
| MenuBar | 菜單條,菜單的容器。 |
| Menu | 菜單組件,菜單項的容器。它也是MenuItem的子類,所以可作為菜單項使用。 |
| PopupMenu | 上下文菜單組件(右鍵菜單組件)。 |
| MenuItem | 菜單項組件。 |
| CheckboxMenuItem | 復選框菜單項組件。 |
下圖是常見菜單相關組件繼承體系圖:
菜單相關組件使用:
小技巧:
- 如果要在某個菜單的菜單項之間添加分割線,那么只需要調用Menu的add(new MenuItem("-"));即可;
- 如果要給某個菜單項關聯快捷鍵功能,那么只需要在創建菜單項對象時設置即可,例如給菜單項關聯Ctrl+Shift+Q快捷鍵,只需要:new MenuItem("菜單項名字",new MenuShortCun(KeyEvent.VK_Q,true));
案例1:
代碼演示:
package Package1;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent;public class MenuDemo {Frame frame = new Frame("這是一個窗口容器Frame");//創建菜單條MenuBar menuBar = new MenuBar();//創建菜單組件Menu fileMenu = new Menu("文件");Menu editMenu = new Menu("編輯");Menu formatMenu = new Menu("格式");//創建菜單項組件MenuItem auto = new MenuItem("自動換行");MenuItem copy = new MenuItem("復制");MenuItem paste = new MenuItem("粘貼");MenuItem comment = new MenuItem("注釋",new MenuShortcut(KeyEvent.VK_Q,true)); //關聯快捷鍵ctrl+shift+QMenuItem cancelComment = new MenuItem("取消注釋");//創建文本框TextArea ta = new TextArea(6,40);//組裝視圖public void init() {comment.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {ta.append("您點擊了菜單項:"+e.getActionCommand()+"\n");}});formatMenu.add(comment);formatMenu.add(cancelComment);//組裝編輯菜單editMenu.add(auto);editMenu.add(copy);editMenu.add(paste);editMenu.add(formatMenu);//組裝菜單條menuBar.add(fileMenu);menuBar.add(editMenu);//把菜單條和文本框放入frame中frame.setMenuBar(menuBar);frame.add(ta,BorderLayout.CENTER);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);}public static void main(String[] args) {new MenuDemo().init();} }案例2:
package Package1;import java.awt.*; import java.awt.event.*;public class MenuDemo {Frame frame = new Frame("這是一個窗口容器Frame");//創建文本域TextArea ta = new TextArea("默認顯示的文本\n",6,40);//創建Panel容器Panel p = new Panel();//創建PopupMenuPopupMenu popupMenu = new PopupMenu();//創建菜單項MenuItem comment = new MenuItem("注釋");MenuItem cancelComment = new MenuItem("取消注釋");MenuItem copy = new MenuItem("復制");MenuItem save = new MenuItem("保存");//組裝視圖public void init() {ActionListener listener = new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String actionCommand = e.getActionCommand();ta.append("您點擊了:"+actionCommand+"\n");}};comment.addActionListener(listener);cancelComment.addActionListener(listener);copy.addActionListener(listener);save.addActionListener(listener);//組裝PopupMenupopupMenu.add(comment);popupMenu.add(cancelComment);popupMenu.add(copy);popupMenu.add(save);p.add(popupMenu);//設置Panel的大小p.setPreferredSize(new Dimension(400,100));//給Panel注冊鼠標事件,監聽用戶釋放鼠標的動作,展示菜單p.addMouseListener(new MouseAdapter() {@Overridepublic void mouseReleased(MouseEvent e) {boolean flag = e.isPopupTrigger();if(flag) {//顯示PopupMenupopupMenu.show(p,e.getX(),e.getY());}}});//放置ta和pframe.add(ta,BorderLayout.CENTER);frame.add(p,BorderLayout.SOUTH);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);}public static void main(String[] args) {new MenuDemo().init();} }8、繪圖
很多程序如各種游戲都需要在窗口中繪制各種圖形,除此之外,即使在開發javaEE項目時,有時候也必須“動態”地向客戶端生成各種圖形、圖標,比如圖形驗證碼、統計圖等,這都需要利用AWT地繪圖功能。
8.1、組件繪圖原理
之前我們已經學過很多組件,例如Button、Frame。Checkbox等等,不同的組件,展示出來的圖形都不一樣,其實這些組件展示出來的圖形,其本質就是用AWT的繪圖來完成的。
在AWT中,真正提供繪圖功能的是Graphics對象,那么Component組件和Graphics對象存在什么關系,才能讓Component繪制自身圖形呢?在Component類中,提供了下列三個方法來完成組件圖形的繪制與刷新:
- paint(Graphics g):繪制組件的外觀;
- update(Graphics g):調用內部paint方法,刷新組件外觀;
- repaint():調用update方法,刷新組件外觀。
一般情況下,update和paint方法是由AWT系統負責調用,如果程序要希望系統重新繪制組件,可以調用repaint()方法完成。
8.2、Graphics對象的使用
AWT中提供了Canvas類充當畫布,提供了Graphics類來充當畫筆,通過調用Graphics對象的setColor()方法可以給畫筆設置顏色。
畫圖的步驟:
其實畫圖的核心就在于使用Graphics畫筆在Canvas畫布上畫出什么顏色、什么樣式的圖形,所以核心在畫筆上,下表給出了Graphics類中常用的一些方法:
| setColor(Color c) | 設置顏色 |
| setFont(Font font) | 設置字體 |
| drawLine() | 繪制直線 |
| drawRect() | 繪制矩形 |
| drawRoundRect() | 繪制圓角矩形 |
| drawOval() | 繪制橢圓形 |
| drawPolygon() | 繪制多邊形 |
| drawArc() | 繪制圓弧 |
| drawPolyline() | 繪制折線 |
| fillRect() | 填充矩形區域 |
| fillRoundRect() | 填充圓角矩形區域 |
| fillOval() | 填充橢圓區域 |
| fillPolygon() | 填充多邊形區域 |
| fillArc() | 填充圓弧對應的扇形區域 |
| drawImage() | 繪制位圖 |
案例1——繪制矩形和橢圓:
package Package1;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;public class GraphicsDemo {private final String RECT_SHAPE = "rect";private final String OVAL_SHAPE = "oval";private Frame frame = new Frame("這是一個窗口容器Frame");Button btnRect = new Button("繪制矩形");Button btnOval = new Button("繪制橢圓");//創建Panel,承載按鈕Panel p = new Panel();//定義一個變量,記錄當前要繪制橢圓還是矩形private String shape = "";//自定義類,繼承Canvas類,重寫paint(Graphics g)方法完成畫圖private class MyCanvas extends Canvas {@Overridepublic void paint(Graphics g) {//繪制不同的圖形if(shape.equals(RECT_SHAPE)) {//繪制矩形g.setColor(Color.BLACK); //設置當前畫筆的顏色為黑色g.drawRect(50,100,100,50);}else if(shape.equals(OVAL_SHAPE)) {//繪制橢圓g.setColor(Color.RED); //設置當前畫筆的顏色為紅色g.drawOval(100,50,50,100);}}}//自定義畫布對象MyCanvas drawArea = new MyCanvas();//組裝視圖public void init() {btnRect.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {shape = RECT_SHAPE; //修改標記的值為rectdrawArea.repaint();}});btnOval.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {shape = OVAL_SHAPE; //修改標記的值為rectdrawArea.repaint();}});//設置drawArea的大小drawArea.setPreferredSize(new Dimension(300,200));frame.add(drawArea);p.add(btnRect);p.add(btnOval);frame.add(p,BorderLayout.SOUTH);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);}public static void main(String[] args) {new GraphicsDemo().init();} }Java也可用于開發一些動畫,就是間隔一定的時間(通常小于0.1秒)重新繪制新的圖像,兩次繪制的圖像之間差異較小,肉眼看起來就成了所謂的動畫。為了實現間隔一定時間就重新調用組件的repaint()方法,可以借助于Swing提供的Timer類,Timer類是一個定時器,它有如下一個構造器:Timer(int delay, ActionListener listener):每間隔delay毫秒,系統自動觸發ActionListener監聽器里的事件處理器方法,在方法內部我們就可以調用組件的repaint()方法,完成組件重繪。
案例2——彈球小游戲:
package Package1;import javax.swing.*; import java.awt.*; import java.awt.event.*;public class GraphicsDemo {//創建窗口對象private Frame frame = new Frame("這是一個窗口容器Frame");//設置桌面寬度和高度private final int TABLE_WIDTH = 300;private final int TABLE_HEIGHT = 400;//設置球拍的寬度和高度private final int RACKET_WIDTH = 60;private final int RACKET_HEIGHT = 20;//定義變量,記錄球拍的坐標private int racketX = 120;private final int racketY = 340;//設置小球的大小private final int BALL_SIZE = 16;//定義變量,記錄小球的坐標private int ballX = 120;private int ballY = 20;//定義變量,記錄小球在x和y方向上分別移動的速度private int speedX = 5;private int speedY = 0;//定義變量,表示當前游戲是否已結束private boolean isOver = false;//聲明一個定時器private Timer timer;//自定義一個類繼承Canvas充當畫布private class MyCanvas extends Canvas {@Overridepublic void paint(Graphics g) {//在這里繪制內容if (isOver) {//游戲結束g.setColor(Color.BLUE);g.setFont(new Font("Times", Font.BOLD, 30));g.drawString("游戲結束!", 80, 200);} else {//游戲中//繪制小球g.setColor(Color.RED);g.fillOval(ballX, ballY, BALL_SIZE, BALL_SIZE);//繪制球拍g.setColor(Color.PINK);g.fillRect(racketX, racketY, RACKET_WIDTH, RACKET_HEIGHT);}}}//創建繪畫區域MyCanvas myCanvas = new MyCanvas();//組裝視圖以及游戲邏輯的控制public void init() {//完成球拍坐標的變化KeyListener listener = new KeyAdapter() {@Overridepublic void keyPressed(KeyEvent e) {//獲取當前按下的鍵int keyCode = e.getKeyCode();if (keyCode == KeyEvent.VK_LEFT) {//<- 應該向左移動if (racketX > 0) {racketX -= 10;}}if (keyCode == KeyEvent.VK_RIGHT) {//-> 應該向右移動if (racketX < TABLE_WIDTH - RACKET_WIDTH) {racketX += 10;}}}};MouseMotionListener mouseMotionListener = new MouseMotionAdapter() {@Overridepublic void mouseMoved(MouseEvent e) {//獲取當前鼠標位置int mouseX = e.getX();if (mouseX < (RACKET_WIDTH / 2)) {//設置球拍的位置racketX = 0;} else if (mouseX > (TABLE_WIDTH - RACKET_WIDTH / 2)) {racketX = TABLE_WIDTH - RACKET_WIDTH;} else {racketX = mouseX - (RACKET_WIDTH / 2);}}};//給frame和myCanvas注冊監聽器frame.addKeyListener(listener);myCanvas.addKeyListener(listener);myCanvas.addMouseMotionListener(mouseMotionListener);frame.addMouseMotionListener(mouseMotionListener);//完成小球坐標的變化ActionListener task = new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//更新小球的坐標speedY += 1;ballX += speedX;ballY += speedY;//根據邊界范圍,修正速度if (ballX <= 0 || ballX >= (TABLE_WIDTH - BALL_SIZE)) {speedX = -speedX; //反向運動}if (ballY <= 0 || (ballY > (racketY - BALL_SIZE) &&ballX >= (racketX - BALL_SIZE) && (ballX <= racketX + RACKET_WIDTH))) {speedY = -speedY;}if (ballY > (racketY - BALL_SIZE) &&(ballX < (racketX - BALL_SIZE) || ballX > (racketX + RACKET_WIDTH))) {//當小球超出了球拍的范圍,游戲結束//停止定時器timer.stop();//修改游戲是否結束的標記isOver = true;}//重繪界面myCanvas.repaint();}};timer = new Timer(30, task);timer.start();//myCanvas.setPreferredSize(new Dimension(TABLE_WIDTH, TABLE_HEIGHT));frame.add(myCanvas);//通過pack()方法設置最佳大小frame.pack();//設置Frame可見frame.setVisible(true);}public static void main(String[] args) {new GraphicsDemo().init();} }8.3、處理位圖
如果僅僅繪制一些簡單的幾何圖形,程序的圖形效果依然比較單調。AWT也允許在組件上繪制位圖,Graphics提供了drawImage(Image image)方法用于繪制位圖,該方法需要一個Image參數代表位圖,通過該方法就可以繪制出指定的位圖。
位圖使用步驟:
使用位圖繪制組件的好處:
使用位圖來繪制組件,相當于實現類圖的緩沖區,此時繪圖時沒有直接把圖形繪制到組件上,而是先繪制到內存中的BufferedImage上,等全部繪制完畢,再一次性地將圖像顯示到組件上即可,這樣用戶的體驗會好一些。
代碼演示:
package Package1;import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage;public class ImageDemo {//定義窗口對象Frame frame = new Frame("這是一個窗口容器Frame");//定義畫圖區的寬高private final int AREA_WIDTH = 500;private final int AREA_HEIGHT = 400;//定義一個右鍵菜單,用于設置畫筆的顏色private PopupMenu colorMenu = new PopupMenu();private MenuItem redItem = new MenuItem("紅色");private MenuItem greenItem = new MenuItem("綠色");private MenuItem blueItem = new MenuItem("藍色");//定義一個變量,記錄當前畫筆的顏色,默認為黑色private Color forceColor = Color.BLACK;//創建一個BufferedImage位圖對象BufferedImage image = new BufferedImage(AREA_WIDTH, AREA_HEIGHT, BufferedImage.TYPE_INT_RGB);//通過位圖,獲取關聯的Graphics對象Graphics g = image.getGraphics();//自定義一個類,繼承Canvasprivate class MyCanvas extends Canvas {@Overridepublic void paint(Graphics g) {g.drawImage(image, 0, 0, null);}}MyCanvas myCanvas = new MyCanvas();//定義變量,記錄鼠標拖動過程中,上一次所處的坐標private int preX = -1;private int preY = -1;//組裝視圖public void init() {ActionListener listener = new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String actionCommand = e.getActionCommand();switch (actionCommand) {case "紅色":forceColor = Color.RED;break;case "綠色":forceColor = Color.GREEN;break;case "藍色":forceColor = Color.BLUE;break;}}};redItem.addActionListener(listener);greenItem.addActionListener(listener);blueItem.addActionListener(listener);colorMenu.add(redItem);colorMenu.add(greenItem);colorMenu.add(blueItem);//把colorMenu設置給繪圖區域myCanvasmyCanvas.add(colorMenu);myCanvas.addMouseListener(new MouseAdapter() {@Override//當鼠標鍵抬起時被調用public void mouseReleased(MouseEvent e) {boolean popupTrigger = e.isPopupTrigger();if (popupTrigger) {colorMenu.show(myCanvas, e.getX(), e.getY());}//重置preX和preYpreX = -1;preY = -1;}});//設置位圖的背景為白色g.setColor(Color.WHITE);g.fillRect(0, 0, AREA_WIDTH, AREA_HEIGHT);//通過監聽鼠標的移動,完成繪制myCanvas.addMouseMotionListener(new MouseMotionAdapter() {@Override//該方法,當鼠標左鍵按下,并進行拖動時,會被調用public void mouseDragged(MouseEvent e) {if (preX > 0 && preY > 0) {g.setColor(forceColor);//畫線條,需要兩組坐標,分別代表線條的起點和終點g.drawLine(preX, preY, e.getX(), e.getY());}//修正preX和preY的值preX = e.getX();preY = e.getY();//重繪組件myCanvas.repaint();}});myCanvas.setPreferredSize(new Dimension(AREA_WIDTH, AREA_HEIGHT));frame.add(myCanvas);frame.pack();frame.setVisible(true);}public static void main(String[] args) {new ImageDemo().init();} }8.4、ImageIO的使用
在實際生活中,很多軟件都支持打開本地磁盤已經存在的圖片,然后進行編輯,編輯完畢后,再重新保存到本地磁盤。如果使用AWT要文成這樣的功能,那么需要使用到ImageIO這個類,可以操作本地磁盤的圖片文件。
| static BufferedImage read(File input) | 讀取本地磁盤圖片文件 |
| static BufferedImage read(InputStream input) | 讀取本地磁盤圖片文件 |
| static boolean write(RenderedImage img, String formatName, File output) | 往本地磁盤中輸出圖片文件 |
代碼演示:
package Package1;import javax.imageio.ImageIO; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException;public class LASImage {private Frame frame = new Frame("這是一個窗口容器Frame");MenuBar menuBar = new MenuBar();Menu menu = new Menu("文件");MenuItem open = new MenuItem("打開");MenuItem save = new MenuItem("另存為");//聲明BufferedImage對象,記錄本地讀取到內存中的圖片BufferedImage image;private class MyCanvas extends Canvas {@Overridepublic void paint(Graphics g) {g.drawImage(image,0,0,null);}}MyCanvas myCanvas = new MyCanvas();//組裝視圖public void init() {open.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//打開一個文件對話框FileDialog fileDialog = new FileDialog(frame,"打開圖片",FileDialog.LOAD);fileDialog.setVisible(true);//獲取用戶選擇的圖片路徑以及名稱String directory = fileDialog.getDirectory();String file = fileDialog.getFile();try {image = ImageIO.read(new File(directory,file));myCanvas.repaint();} catch (IOException ex) {ex.printStackTrace();}}});save.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//展示一個文件對話框FileDialog fileDialog = new FileDialog(frame,"保存圖片",FileDialog.SAVE);fileDialog.setVisible(true);//獲取用戶設置的保存路徑以及文件名稱String directory = fileDialog.getDirectory();String file = fileDialog.getFile();try {ImageIO.write(image,"JPEG",new File(directory,file));} catch (IOException ex) {ex.printStackTrace();}}});menu.add(open);menu.add(save);menuBar.add(menu);frame.setMenuBar(menuBar);frame.add(myCanvas);//通過pack()方法設置最佳大小frame.pack();//設置Frame的位置和大小frame.setBounds(400,200,500,300);//設置Frame可見frame.setVisible(true);}public static void main(String[] args) {new LASImage().init();} }總結
以上是生活随笔為你收集整理的Java图形化界面编程之——AWT的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 迅捷CAD编辑器使用的感觉还不错
- 下一篇: arm export 汇编_灵活使用AR