自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

簡述Java圖形用戶界面設(shè)計(jì)(Swing)

開發(fā) 后端
Swing是一個(gè)用于開發(fā)Java應(yīng)用程序用戶界面的開發(fā)工具包。它以抽象窗口工具包(AWT)為基礎(chǔ)使跨平臺(tái)應(yīng)用程序可以使用任何可插拔的外觀風(fēng)格。Swing開發(fā)人員只用很少的代碼就可以利用Swing豐富、靈活的功能和模塊化組件來創(chuàng)建優(yōu)雅的用戶界面。

作為一個(gè) Java 程序員,從論壇上感受到使用 Java 開發(fā)程序的人越來多,心中不免欣慰。但是,同樣是從論壇中,看到多數(shù)人提到 Java 就以為是網(wǎng)絡(luò)開發(fā)——不是這樣的,Java 也可以開發(fā)應(yīng)用程序,而且可以開發(fā)出漂亮的圖形用戶界面的應(yīng)用程序,也就是 Windows/XWindow 應(yīng)用程序。因此,我寫下這篇文章,希望能帶你進(jìn)入 Java 圖形用戶界面設(shè)計(jì)之門。

下面,讓我們開始……

說明:所有代碼均在 Windows XP + Eclipse 環(huán)境下編寫和測(cè)試,示例圖片均在 Windows XP 下捕捉。

目  錄

一. AWT 和 Swing

二. 框架、監(jiān)聽器和事件

三. 按鈕、切換按鈕、復(fù)選按鈕和單選按鈕

四. 文本輸入框、密碼輸入框

五. 窗格、滾動(dòng)窗格和布局管理

六. 后記——什么是 SWT (2006年補(bǔ)充)


一. AWT 和 Swing

AWT 和 Swing 是 Java 設(shè)計(jì) GUI 用戶界面的基礎(chǔ)。與 AWT 的重量級(jí)組件不同,Swing 中大部分是輕量級(jí)組件。正是這個(gè)原因,Swing 幾乎無所不能,不但有各式各樣先進(jìn)的組件,而且更為美觀易用。所以一開始使用 AWT 的程序員很快就轉(zhuǎn)向使用 Swing 了。

那為什么 AWT 組件沒有消亡呢?因?yàn)?Swing 是架構(gòu)在 AWT 之上的,沒有 AWT 就沒有 Swing。所以程序員可以根據(jù)自己的習(xí)慣選擇使用 AWT 或者是 Swing。但是,最好不要二者混用——除開顯示風(fēng)格不同不說,還很可能造成層次 (Z-Order) 錯(cuò)亂,比如下例:

  1. /*  
  2.  * AwtSwing.java  
  3.  * @author Fancy  
  4.  */ 
  5.  
  6. import java.awt.BorderLayout;  
  7. import java.awt.Button;  
  8.  
  9. import javax.swing.JButton;  
  10. import javax.swing.JDesktopPane;  
  11. import javax.swing.JFrame;  
  12. import javax.swing.JInternalFrame;  
  13. import javax.swing.JPanel;  
  14.  
  15. public final class AwtSwing {  
  16.  
  17.     public static void main(String[] args) {  
  18.         AwtSwing as = new AwtSwing();  
  19.         as.show();  
  20.     }  
  21.  
  22.     JFrame frame = new JFrame("Test AWT and SWING");  
  23.  
  24.     JDesktopPane jdp = new JDesktopPane();  
  25.  
  26.     JInternalFrame jif1 = new JInternalFrame("controls");  
  27.  
  28.     JInternalFrame jif2 = new JInternalFrame("cover");  
  29.  
  30.     public AwtSwing() {  
  31.         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  32.         frame.getContentPane().add(jdp);  
  33.  
  34.         jif1.setContentPane(new JPanel());  
  35.         jif2.setContentPane(new JPanel());  
  36.  
  37.         jif1.getContentPane().setLayout(new BorderLayout());  
  38.         jif1.getContentPane().add(new Button("AWT Button"), BorderLayout.WEST);  
  39.         jif1.getContentPane().add(new JButton("Swing Button"),  
  40.                 BorderLayout.EAST);  
  41.  
  42.         jif1.setSize(200100);  
  43.         jif2.setSize(200100);  
  44.  
  45.         jdp.add(jif1);  
  46.         jdp.add(jif2);  
  47.  
  48.         frame.setSize(240140);  
  49.     }  
  50.  
  51.     public void show() {  
  52.         frame.setVisible(true);  
  53.         jif1.setVisible(true);  
  54.         jif2.setVisible(true);  
  55.     }  
  56.  

運(yùn)行這個(gè)程序,并用鼠標(biāo)拖動(dòng)那個(gè)名為“cover”的子窗口,我們會(huì)發(fā)現(xiàn)一個(gè)非常有趣的現(xiàn)象,如圖:

顯然 cover 子窗口是在 controls 子窗口之上的,但是它只罩蓋住了 Swing Button,沒有罩蓋住 AWT Button。再看一會(huì)兒,你是不是有這樣一種感覺:Swing Button 是“畫”上去的,而 AWT Button 則是“貼”上去的。這就是二者混用造成層次錯(cuò)亂的一個(gè)例子。

Swing 組件有美觀、易用、組件量大等特點(diǎn),也有缺點(diǎn)——使用 Swing 組件的程序通常會(huì)比使用 AWT 組件的程序運(yùn)行更慢。但是大家都還是更喜歡用 Swing 組件,原因何在?因?yàn)殡S著計(jì)算機(jī)硬件的升級(jí),一點(diǎn)點(diǎn)速度已經(jīng)不是問題。相反的,用戶更需要美觀的用戶界面,開發(fā)人員則更需要易用的開發(fā)組件。

二. 框架、監(jiān)聽器和事件

框架 (Frame, JFrame) 是 Java 圖形用戶界面的基礎(chǔ),它就是我們通常所說的窗口,是 Windows/XWindow 應(yīng)用程序的典型特征。說到 Windows/XWindow,大家很容易聯(lián)想到“事件 (Event) 驅(qū)動(dòng)”。Java 的圖形用戶界面正是事件驅(qū)動(dòng)的,并且由各種各樣的監(jiān)聽器 (Listener) 負(fù)責(zé)捕捉各種事件。

如果我們需要對(duì)某一個(gè)組件的某種事件進(jìn)行捕捉和處理時(shí),就需要為其添加監(jiān)聽器。比如,我們要在一個(gè)窗口 (JFrame) 激活時(shí)改變它的標(biāo)題,我們就需要為這個(gè)窗口 (JFrame 對(duì)象) 添加一個(gè)可以監(jiān)聽到“激活窗口”這一事件的監(jiān)聽器——WindowListener。

怎么添加監(jiān)聽器呢?這通常由組件類提供的一個(gè) addXxxxxListener 的方法來完成。比如 JFrame 就提供有 addWindowListener 方法添加窗口監(jiān)聽器 (WindowListener)。

一個(gè)監(jiān)聽器常常不只監(jiān)聽一個(gè)事件,而是可以監(jiān)聽相關(guān)的多個(gè)事件。比如 WindowListener 除了監(jiān)聽窗口激活事件 (windowActivate) 之外,還可以監(jiān)聽窗口關(guān)閉事件 (windowClosing) 等。那么這些事件怎么區(qū)分呢?就靠重載監(jiān)聽器類 (Class) 的多個(gè)方法 (Method) 了。監(jiān)聽器監(jiān)聽到某個(gè)事件后,會(huì)自動(dòng)調(diào)用相關(guān)的方法。因此我們只要重載這個(gè)方法,就可以處理相應(yīng)的事件了。

不妨先看一個(gè)例子:

  1. /**  
  2.  * @(#) TestFrame.java  
  3.  * @author James  
  4.  */ 
  5.  
  6. import javax.swing.*;  
  7. import java.awt.event.*;  
  8.  
  9. public class TestFrame extends JFrame {  
  10.  
  11.     private int counter = 0;  
  12.  
  13.     public TestFrame() {  
  14.         /* 使用匿名類添加一個(gè)窗口監(jiān)聽器 */ 
  15.         addWindowListener(new WindowAdapter() {  
  16.             public void windowClosing(WindowEvent e) {  
  17.                 System.out.println(  
  18.                     "Exit when Closed event");  
  19.                 //退出應(yīng)用程序  
  20.                 System.exit(0);  
  21.             }  
  22.  
  23.             public void windowActivated(WindowEvent e) {  
  24.                 // 改變窗口標(biāo)題  
  25.                 setTitle("Test Frame " + counter++);  
  26.             }  
  27.         });  
  28.  
  29.         // 設(shè)置窗口為固定大小  
  30.         setResizable(false);  
  31.         setSize(200150);  
  32.     }  
  33.  
  34.     public static void main(String[] args) {  
  35.         TestFrame tf = new TestFrame();  
  36.         tf.show();  
  37.     }  
  38.  

這個(gè)例子中,我們?cè)O(shè)計(jì)了一個(gè)窗口類(public class TestFrame extends JFrame { ... }),并且為這個(gè)窗口添加了一個(gè)窗口監(jiān)聽器 (addWindowListener(new WindowAdapter() ...)。而我們添加的這個(gè)窗口監(jiān)聽器主要監(jiān)聽了兩個(gè)事件:窗口關(guān)閉 (public void windowClosing(WindowEvent e) ...) 和窗口激活 (public void windowActivated(WindowEvent e) ...)。在窗口關(guān)閉事件中我們退出了整個(gè)應(yīng)用程序(System.exit(0);),而在窗口激活事件中,我們改變了窗口的標(biāo)題 (setTitle("Test Frame " + counter++);)。最后,我們?cè)?main 方法中顯示了這窗口類的一個(gè)實(shí)例,運(yùn)行得到下圖所示的結(jié)果:

這個(gè)程序的運(yùn)行結(jié)果就是一個(gè)什么東西都沒有加的框架,也就是一個(gè)空窗口。那么,你知道顯示一個(gè)窗口最主要的幾句代碼嗎?不知道沒關(guān)系,我來告訴你,顯示一個(gè)窗口只需要做三件事:生成實(shí)例(對(duì)象)→設(shè)置大小→顯示,相應(yīng)的,就是下面的三句代碼:

  1. JFrame frame = new JFrame("Frame's Title");  
  2. frame.setSize(400300);  
  3. frame.setVisible(true); 

也許你會(huì)說:第一句的意思我清楚,第三句的意思我也明白,為什么一定要第二句呢?其實(shí)想想也就明白了,叫你畫一個(gè)沒法有大小的矩形你能畫出來嗎?不能。同樣,沒有大小的窗口,怎么顯示?所以我們需要用 setSize(int width, int height) 方法為其設(shè)置大小。我們還有另一種方法:用 JFrame 的 pack() 方法讓它自己適配一個(gè)大小。pack() 在多數(shù)時(shí)候是令人滿意的,但有時(shí),它也會(huì)讓你哭笑不得——多試試就知道了。

在 JFrame 中,我們使用 addWindowListener 方法加入一個(gè)監(jiān)聽器 WindowListener (addWindowListener(new WindowAdapter() ...) 去監(jiān)聽發(fā)生在 JFrame 上的窗口事件。WindowListener 是一個(gè)接口,在 java.awt.event 這個(gè)包中,但是上例中好象并沒有使用 WindowListener,而是使用的 WindowsAdapter 吧,這是怎么回事?

WindowAdapter 是 WindowsListener 接口的一個(gè)最簡單的實(shí)現(xiàn),也在 java.awt.event 包中。如果我們直接使用 WindowListener 產(chǎn)生一個(gè)類,需要實(shí)現(xiàn)它的每一個(gè)方法 (一共 7 個(gè))。但 WindowAdapter 作為 WindowListener 最簡單的實(shí)現(xiàn),已經(jīng)實(shí)現(xiàn)了它的每一個(gè)方法為空方法 (即只包含空語句,或者說沒有語句的方法)。用 WindowAdapter 就只需要重載可能用到的方法 (上例中只有 2 個(gè)) 就行了,而不需要再去實(shí)現(xiàn)每一個(gè)方法。優(yōu)點(diǎn)顯而易見——減少編碼量。

在 JFrame 上發(fā)生的窗口事件 (WindowEvent) 包括:

windowActivated(WindowEvent e)
窗口得到焦點(diǎn)時(shí)觸發(fā)
windowClosed(WindowEvent e)
窗口關(guān)閉之后觸發(fā)
windowClosing(WindowEvent e)
窗口關(guān)閉時(shí)觸發(fā)
windowDeactivated(WindowEvent e)
窗口失去焦點(diǎn)時(shí)觸發(fā)
windowDeiconified(WindowEvent e)
 
windowIconified(WindowEvent e)
 
windowOpened(WindowEvent e)
窗口打開之后觸發(fā)

上例重載了其中兩個(gè)方法。如果在上例運(yùn)行產(chǎn)生的窗口和另外一個(gè)應(yīng)用程序窗口之間來回切換 (在 Windows 操作系統(tǒng)中你可以使用 Alt+Tab 進(jìn)行切換)……試試看,你發(fā)現(xiàn)了什么?有沒有現(xiàn)我們的示例窗口標(biāo)題上的數(shù)字一直在增加,這便是在 windowActivated 事件中 setTitle("Test Frame " + counter++) 的功勞。

而另一個(gè)事件處理函數(shù) windowClosing 中的 System.exit(0) 則保證了當(dāng)窗口被關(guān)閉時(shí)退出當(dāng)前的 Java 應(yīng)用程序。如果不作這樣的處理會(huì)怎樣呢?試驗(yàn)之后你會(huì)發(fā)現(xiàn),窗口雖然關(guān)閉了,但程序并沒有結(jié)束,但此時(shí),除了使用 Ctrl+C 強(qiáng)行結(jié)束之外,恐怕也沒有其它辦法了。所以,這一點(diǎn)非常重要:你想在關(guān)閉窗口的時(shí)候退出應(yīng)用程序,那就需要處理 windowClosing 事件。……也不盡然,其實(shí)還有另外一個(gè)更簡單的辦法,讓 JFrame 自己處理這件事——你只需要如下調(diào)用 JFrame 的 setDefaultCloseOperation 即可:

  1. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

在產(chǎn)生 JFrame 對(duì)象之后執(zhí)行上述語句,就可以不用處理 windowsClosing 事件來退出程序了。

我們可以在 JFrame 對(duì)象中添加 AWT 或者 Swing 組件。但是,雖然它有 add 方法,卻不能直接用于添加組件,否則崤壯鲆斐!恍啪褪允浴T斐燒飧魷窒蟮腦蛑揮幸桓黿饈停篔Frame 不是一個(gè)容器,它只是一個(gè)框架。那么,應(yīng)該怎么添加組件呢?

JFrame 有一個(gè) Content Pane,窗口是顯示的所有組件都是添加在這個(gè) Content Pane 中。JFrame 提供了兩個(gè)方法:getContentPane 和 setContentPane 就是用于獲取和設(shè)置其 Content Pane 的。通常我們不需要重新設(shè)置 JFrame 的 Content Pane,只需要直接獲取默認(rèn)的 Content Pane 來添加組件等。如:

  1. (new JFrame()).getContentPane().add(new Button("test button")) 

三. 按鈕、切換按鈕、復(fù)選按鈕和單選按鈕

按鈕……就是按鈕,不會(huì)連按鈕都不知道吧?

切換按鈕,有兩種狀態(tài)的按鈕,即按下狀態(tài)和彈起狀態(tài),若稱為選擇狀態(tài)或未選擇狀態(tài)。

復(fù)選按鈕,又叫復(fù)選框,用一個(gè)小方框中是否打勾來表示兩種狀態(tài)。

單選按鈕,又叫收音機(jī)按鈕,以小圓框打點(diǎn)表示被選中。常成組出現(xiàn),一組單選按鈕中只有一個(gè)能被選中。

發(fā)現(xiàn)什么了嗎?——對(duì)了,這一部分是在講各種各樣的按鈕,而且后三種按鈕都有兩種狀態(tài)。先看看這些按鈕都長成什么樣:

上圖中,從上到下,依次就是按鈕、切換按鈕、復(fù)選按鈕和單選按鈕。圖示的窗口,就是下面這個(gè)例子的運(yùn)行結(jié)果:

  1. /*  
  2.  * TestButtons.java  
  3.  * @author Fancy  
  4.  */ 
  5.  
  6. import java.awt.event.ActionEvent;  
  7. import java.awt.event.ActionListener;  
  8. import java.awt.event.ItemEvent;  
  9. import java.awt.event.ItemListener;  
  10.  
  11. import javax.swing.ButtonGroup;  
  12. import javax.swing.JButton;  
  13. import javax.swing.JCheckBox;  
  14. import javax.swing.JFrame;  
  15. import javax.swing.JLabel;  
  16. import javax.swing.JRadioButton;  
  17. import javax.swing.JToggleButton;  
  18.  
  19. public final class TestButtons {  
  20.  
  21.     public static void main(String[] args) {  
  22.         TestButtons tb = new TestButtons();  
  23.         tb.show();  
  24.     }  
  25.  
  26.     JFrame frame = new JFrame("Test Buttons");  
  27.  
  28.     JButton jButton = new JButton("JButton"); // 按鈕  
  29.  
  30.     JToggleButton toggle = new JToggleButton("Toggle Button"); // 切換按鈕  
  31.  
  32.     JCheckBox checkBox = new JCheckBox("Check Box"); // 復(fù)選按鈕  
  33.  
  34.     JRadioButton radio1 = new JRadioButton("Radio Button 1"); // 單選按鈕  
  35.  
  36.     JRadioButton radio2 = new JRadioButton("Radio Button 2");  
  37.  
  38.     JRadioButton radio3 = new JRadioButton("Radio Button 3");  
  39.  
  40.     JLabel label = new JLabel("Here is Status, look here."); // 不是按鈕,是靜態(tài)文本  
  41.  
  42.     public TestButtons() {  
  43.         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  44.         frame.getContentPane().setLayout(new java.awt.FlowLayout());  
  45.  
  46.         // 為一般按鈕添加動(dòng)作監(jiān)聽器  
  47.         jButton.addActionListener(new ActionListener() {  
  48.             public void actionPerformed(ActionEvent ae) {  
  49.                 label.setText("You clicked jButton");  
  50.             }  
  51.         });  
  52.  
  53.         // 為切換按鈕添加動(dòng)作監(jiān)聽器  
  54.         toggle.addActionListener(new ActionListener() {  
  55.             public void actionPerformed(ActionEvent ae) {  
  56.                 JToggleButton toggle = (JToggleButton) ae.getSource();  
  57.                 if (toggle.isSelected()) {  
  58.                     label.setText("You selected Toggle Button");  
  59.                 } else {  
  60.                     label.setText("You deselected Toggle Button");  
  61.                 }  
  62.             }  
  63.         });  
  64.  
  65.         // 為復(fù)選按鈕添加條目監(jiān)聽器  
  66.         checkBox.addItemListener(new ItemListener() {  
  67.             public void itemStateChanged(ItemEvent e) {  
  68.                 JCheckBox cb = (JCheckBox) e.getSource();  
  69.                 label.setText("Selected Check Box is " + cb.isSelected());  
  70.             }  
  71.         });  
  72.  
  73.         // 用一個(gè)按鈕組對(duì)象包容一組單選按鈕  
  74.         ButtonGroup group = new ButtonGroup();  
  75.         // 生成一個(gè)新的動(dòng)作監(jiān)聽器對(duì)象,備用  
  76.         ActionListener al = new ActionListener() {  
  77.             public void actionPerformed(ActionEvent ae) {  
  78.                 JRadioButton radio = (JRadioButton) ae.getSource();  
  79.                 if (radio == radio1) {  
  80.                     label.setText("You selected Radio Button 1");  
  81.                 } else if (radio == radio2) {  
  82.                     label.setText("You selected Radio Button 2");  
  83.                 } else {  
  84.                     label.setText("You selected Radio Button 3");  
  85.                 }  
  86.             }  
  87.         };  
  88.         // 為各單選按鈕添加動(dòng)作監(jiān)聽器  
  89.         radio1.addActionListener(al);  
  90.         radio2.addActionListener(al);  
  91.         radio3.addActionListener(al);  
  92.         // 將單選按鈕添加到按鈕組中  
  93.         group.add(radio1);  
  94.         group.add(radio2);  
  95.         group.add(radio3);  
  96.  
  97.         frame.getContentPane().add(jButton);  
  98.         frame.getContentPane().add(toggle);  
  99.         frame.getContentPane().add(checkBox);  
  100.         frame.getContentPane().add(radio1);  
  101.         frame.getContentPane().add(radio2);  
  102.         frame.getContentPane().add(radio3);  
  103.         frame.getContentPane().add(label);  
  104.  
  105.         frame.setSize(200250);  
  106.     }  
  107.  
  108.     public void show() {  
  109.         frame.setVisible(true);  
  110.     }  
  111.  

除一般按鈕外,其余三種按鈕都有兩種狀態(tài),即選擇 (按下) 狀態(tài)和未選擇 (彈起) 狀態(tài)。那么我們又該如何判斷呢?切換按鈕 (JToggleButton) 提供了一個(gè) isSelected() 方法用來判斷當(dāng)前所處的狀態(tài),返回值為真 (true) 時(shí)表示它處于選擇狀態(tài),返回值為假 (false) 時(shí)表示它處于未選擇狀態(tài)。而復(fù)選按鈕 (JCheckBox) 和單選按鈕 (JRadioButton) 都是從 JToggleButton 繼承的,所以也具有 isSelected() 方法。如上例中 if (toggle.isSelected()) { ... } 等。

單選按鈕由自身的特點(diǎn)決定了它們必須成組出現(xiàn),而且一組中只能有一個(gè)能被選中。因此我們需要用一個(gè)專門的類,ButtonGroup 來管理。添加到 ButtonGroup 的多個(gè)單選按鈕中,如果有一個(gè)被選擇中,同組中的其它單選按鈕都會(huì)自動(dòng)改變其狀態(tài)為未選擇狀態(tài)。在 ButtonGroup 中添加按鈕,是使用它的 add 方法,如上例中的 group.add(radio1);。

既然我們已經(jīng)將多個(gè)單選按鈕添加到一個(gè) ButtonGroup 中了,那么我們是不是可以將一個(gè)包含多個(gè)單選按鈕的 ButtonGroup 對(duì)象添加到 JFrame 的 Content Pane 中,以達(dá)到添加其中所有單選按鈕的目的呢?不行!ButtonGroup 不是一個(gè)可顯示的組件,它僅用于管理。所以,在往 JFrame 中添加一組 JRadioButton 的時(shí)候,需要一個(gè)一個(gè)的添加 JRadioButton,而不是籠統(tǒng)的添加一個(gè) ButtonGroup。

上例中還用到了 JLabel,這不是按鈕,而是一個(gè)靜態(tài)文本組件,主要用于顯示提示文本。要獲得一個(gè) JLabel 對(duì)象當(dāng)前顯示的文本內(nèi)容,可以使用它的 getText() 方法;反之,要改變一個(gè) JLabel 對(duì)象顯示的文本,要使用它的 setText(String text) 方法,如上例中的 label.setText("You selected Toggle Button")。

其實(shí)這兩個(gè)方法同樣可以用于 JButton 等類。比如上例中我們使用 new JButton("JButton") 構(gòu)造了一個(gè)按鈕 jButton,如果使用 jButton.getText() 就可以得到字符串 "JButton"。而 jButton.setText("A Button"),則可以改變按鈕上顯示的文字為 "A Button"。這兩句代碼沒有在示例中寫出來,你可以自己試試。

上例中大量使用了動(dòng)作監(jiān)聽器 (ActionListener)。ActionListener 只監(jiān)聽一個(gè)事件,這個(gè)事件在其相關(guān)組件上產(chǎn)生了動(dòng)作時(shí)被觸發(fā),因此叫作動(dòng)作事件 (ActionEvent)。ActionListener 只有一個(gè)方法需要實(shí)現(xiàn),就是 actionPerformed(ActionEvent event)。按鈕、切換按鈕和單選按鈕被單擊時(shí)都會(huì)觸發(fā)動(dòng)作事件,引起動(dòng)作監(jiān)聽器調(diào)用 actionPerformed 方法。因此,如果你想在單擊按鈕之后做什么事,當(dāng)然應(yīng)該重載 ActionListener 的 actionPerformed 方法了。各種按鈕都提供了 addActionListener 方法以添加動(dòng)作監(jiān)聽器。

復(fù)選框就要特殊一些。雖然它也有 addActionListener 方法,意味著可以使用動(dòng)作監(jiān)聽器,但是使用之后你會(huì)發(fā)現(xiàn)動(dòng)作監(jiān)聽器并沒有起到預(yù)想的作用。為什么?原來,單擊一個(gè)復(fù)選按鈕,觸發(fā)的不是動(dòng)作事件,而是條目事件 (ItemEvent) 中的狀態(tài)變化 (itemStateChanged),由條目監(jiān)聽器 (ItemListener) 監(jiān)聽,相應(yīng)需要重載的方法是 ItemListener 的 itemStateChanged 方法。

上例中我們將一個(gè)名為 al 的 ActionListener 添加到了每一個(gè)單選按鈕中,如何判斷是哪個(gè)單選按鈕觸發(fā)了事件并被 al 監(jiān)聽到了呢?我們可以從 ActionEvent 的 getSource() 方法得到觸發(fā)事件單選按鈕。由于 getSource() 返回的是一個(gè) Object 引用,雖然這個(gè)引用指向的是一個(gè)單選按鈕的實(shí)例,但我們還是需要將這個(gè)引用的類型轉(zhuǎn)換為 JRadioButton,如上例中的:JRadioButton radio = (JRadioButton) ae.getSource(),只有這樣我們才能調(diào)用 JRadioButton 有而 Object 沒有的方法。

同時(shí),還需要說明的一點(diǎn)是,每個(gè)單選按鈕都可以添加一個(gè)單獨(dú)的 ActionListener 實(shí)例,而不一定要添加同一個(gè)。同樣的道理,若干毫不相干的、需要添加 ActionListener 的若干組件,也可以添加同一個(gè) ActionListener 實(shí)例。關(guān)鍵在于編程者對(duì) actionPerformed 方法的重載。比如下面這段代碼就為一個(gè) JButton 對(duì)象和一個(gè) JRadioButton 對(duì)象添加了同一個(gè)動(dòng)作監(jiān)聽器實(shí)例:

  1. /**  
  2.  * @(#) Test.java  
  3.  * @author James  
  4.  */ 
  5.  
  6. import javax.swing.*;  
  7. import java.awt.event.*;  
  8.  
  9. public class Test {  
  10.  
  11.     JButton b;  
  12.     JRadioButton rb;  
  13.  
  14.     public Test() {  
  15.         JFrame f = new JFrame("Test");  
  16.         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  17.         f.getContentPane().setLayout(  
  18.             new java.awt.FlowLayout());  
  19.         b = new JButton("JButton");  
  20.         rb = new JRadioButton("RadioButton");  
  21.         ActionListener a = new ActionListener() {  
  22.             public void actionPerformed(ActionEvent ae) {  
  23.                 if (ae.getSource() == b) {  
  24.                     System.out.println(  
  25.                         "You clicked the JButton");  
  26.                 } else {  
  27.                     System.out.println(  
  28.                         "You clicked the RadioButton");  
  29.                 }  
  30.             }  
  31.         };  
  32.         b.addActionListener(a);  
  33.         rb.addActionListener(a);  
  34.         f.getContentPane().add(b);  
  35.         f.getContentPane().add(rb);  
  36.         f.pack();  
  37.         f.show();  
  38.     }  
  39.  
  40.     public static void main(String[] args) {  
  41.         new Test();  
  42.     }  
  43.  

運(yùn)行程序后,分別單擊兩個(gè)按鈕,相應(yīng)的,在控制臺(tái)能分別得到如下輸出:

You clicked the JButton 
You clicked the RadioButton

這說明多個(gè)不用的組件添加同一個(gè)監(jiān)聽器是可行的——不過前提是這些組件都能添加這個(gè)監(jiān)聽器。

四. 文本輸入框、密碼輸入框

文本輸入框包括兩種,單行文本輸入框 (JTextField) 和多行文本輸入框 (JTextArea)。密碼輸入框則只有一種 (JPasswordField)。JPasswordField 是 JTextField 的子類,它們的主要區(qū)別是 JPasswordField 不會(huì)顯示出用戶輸入的東西,而只會(huì)顯示出程序員設(shè)定的一個(gè)固定字符,比如 '*' 或者 '#'。

下面的示例圖和代碼是 JTextField、JPasswordField 和 JTextArea 的示例:

  1. /*  
  2.  * TestTexts.java  
  3.  * @author Fancy  
  4.  */ 
  5.  
  6. import javax.swing.JFrame;  
  7. import javax.swing.JLabel;  
  8. import javax.swing.JPasswordField;  
  9. import javax.swing.JTextArea;  
  10. import javax.swing.JTextField;  
  11. import javax.swing.event.CaretEvent;  
  12. import javax.swing.event.CaretListener;  
  13.  
  14. public final class TestTexts extends JFrame {  
  15.  
  16.     public static void main(String[] args) {  
  17.         TestTexts tt = new TestTexts();  
  18.         tt.setVisible(true);  
  19.     }  
  20.  
  21.     private JLabel label = new JLabel("Status");  
  22.  
  23.     private JTextField textField;  
  24.  
  25.     private JPasswordField pwdField;  
  26.  
  27.     private JTextArea textArea;  
  28.  
  29.     public TestTexts() {  
  30.         super("Test Texts");  
  31.         setDefaultCloseOperation(EXIT_ON_CLOSE);  
  32.         getContentPane().setLayout(new java.awt.FlowLayout());  
  33.  
  34.         textField = new JTextField(15);  
  35.         /* 監(jiān)聽文本光標(biāo)移動(dòng)事件 */ 
  36.         textField.addCaretListener(new CaretListener() {  
  37.             public void caretUpdate(CaretEvent e) {  
  38.                 // 如果改變了內(nèi)容,就可以即時(shí)更新 label 顯示的內(nèi)容  
  39.                 label.setText(textField.getText());  
  40.             }  
  41.         });  
  42.  
  43.         pwdField = new JPasswordField(15);  
  44.         pwdField.setEchoChar('#');  
  45.  
  46.         textArea = new JTextArea(515);  
  47.         textArea.setLineWrap(true);  
  48.  
  49.         getContentPane().add(textField);  
  50.         getContentPane().add(pwdField);  
  51.         getContentPane().add(textArea);  
  52.         getContentPane().add(label);  
  53.  
  54.         setSize(200200);  
  55.     }  
  56.  

上例中,我們構(gòu)造了一個(gè)寬度為 15 個(gè)字符的單行文本框 (textField = new JTextField(15);),并使用 addCaretListener 方法添加了一個(gè) CaretListener (textField.addCaretListener ...)。CaretListener 監(jiān)聽文本光標(biāo)的移動(dòng)事件。當(dāng)用戶使用鍵盤、鼠標(biāo)等移動(dòng)了文本光標(biāo)在 JTextField 中的位置時(shí)觸發(fā)這個(gè)事件。我們需要重載 caretUpdate(CaretEvent e) 對(duì)事件進(jìn)行處理 (public void caretUpdate(CaretEvent e) ...)。這樣,我們可以在這里做類似 VB 中 TextBox 的 OnChange 事件中做的事情。

JTextField 有 5 個(gè)構(gòu)造方法,常用其中的四個(gè):

JTextField()
JTextField(int columns),如上例 textField = new JTextField(15);
JTextField(String text)
JTextField(String text, int columns)

其中,參數(shù) text 是單行文本框的初始內(nèi)容,而 columns 指定了單行文本框的寬度,以字符為單位。JTextField 中的文本內(nèi)容可以用 getText() 方法獲得。也可以用 setText 方法指定 JTextField 中的文本內(nèi)容。

JPasswordField 是 JTextField 的子類,其構(gòu)造方法也是類似的。JPasswordField 提供了 setEchoChar(char ch) 方法設(shè)置為了隱藏密碼而顯示的字符,默認(rèn)為 '*' 字符,上例中則設(shè)置為了 '#' 字符 (pwdField.setEchoChar('#');)。與 JTextField 一樣,JPasswordField 也用 getText 方法和 setText 獲得或者設(shè)置文本內(nèi)容 (當(dāng)然在用戶界面上是隱藏的)。

JTextField 是單行文本框,不能顯示多行文本,如果想要顯示多行文本,就只好使用多行文本框 JTextArea 了。JTextArea 有六個(gè)構(gòu)造方法,常用的也是四個(gè):

JTextArea()
JTextArea(int rows, int columns)
JTextArea(String text)
JTextArea(String text, int rows, int columns) 

text 為 JTextArea 的初始化文本內(nèi)容;rows 為 JTextArea 的高度,以行為單位;columns 為 JTextArea 的寬度,以字符為單位。如上例中就構(gòu)造了一個(gè)高 5 行,寬 15 個(gè)字符的多行文本框 (textArea = new JTextArea(5, 15);)。

多行文本框默認(rèn)是不會(huì)自動(dòng)折行的 (不過可以輸入回車符換行),我們可以使用 JTextArea 的 setLineWrap 方法設(shè)置是否允許自動(dòng)折行。setLineWrap(true) 是允許自動(dòng)折行,setLineWrap(false) 則是不允許自動(dòng)折行。多行文本框會(huì)根據(jù)用戶輸入的內(nèi)容自動(dòng)擴(kuò)展大小,不信,自己做個(gè)實(shí)驗(yàn)——如果不自動(dòng)折行,那么多行文本框的寬度由最長的一行文字確定的;如果行數(shù)據(jù)超過了預(yù)設(shè)的行數(shù),則多行文本框會(huì)擴(kuò)展自身的高度去適應(yīng)。換句話說,多行文本框不會(huì)自動(dòng)產(chǎn)生滾動(dòng)條。怎么辦?后面講到滾動(dòng)窗格 (JScrollPane) 的時(shí)候,你就知道了。

多行文本框里文本內(nèi)容的獲得和設(shè)置,同樣可以使用 getText 和 setText 兩個(gè)方法來完成。

五. 窗格、滾動(dòng)窗格和布局管理

窗格 (JPanel) 和滾動(dòng)窗格 (JScrollPane) 在圖形用戶界面設(shè)計(jì)中大量用于各種組件在窗口上的布置和安排。這里所謂的布置和安排,就是布局 (Layout),因此不得不先說說布局。

將加入到容器(通常為窗口等) 的組件按照一定的順序和規(guī)則放置,使之看起來更美觀,這就是布局。布局由布局管理器 (Layout Manager) 來管理。那么,我們?cè)谑裁磿r(shí)候應(yīng)該使用布局管理器?應(yīng)用選擇哪種布局管理器?又該怎樣使用布局管理器呢?

往往,我們?cè)O(shè)計(jì)一個(gè)窗口,其中是要添加若干組件的。為了管理好這些管理的布局,我們就要使用布局管理器。比如說,設(shè)計(jì)一個(gè)簡單的編輯器,這個(gè)編輯器中只需要放置兩個(gè)按鈕和一個(gè)多行文本框。這些組件是讓 Java 自己任意安排呢?還是按照一定的位置關(guān)系較規(guī)范的安排呢?當(dāng)然應(yīng)該選擇后者。那么,為了按照一定的位置關(guān)系安排這些組件,我們就需要用到布局管理器了。

然后我們遇到了一個(gè)選擇題——使用哪種布局管理器。為此,我們首先要知道有些什么布局管理器,它們的布局特點(diǎn)是什么。常用的布局管理器有: FlowLayout、BorderLayout、GridLayout、BoxLayout 等,其中 FlowLayout 和 BorderLayout 最常用,本文主要也就只談?wù)勥@兩種布局管理器。下面列表說明它們的布局特點(diǎn):

布局管理器 布局特點(diǎn)

FlowLayout
將組件按從左到右從上到下的順序依次排列,一行不能放完則折到下一行繼續(xù)放置
BorderLayout
將組件按東(右)、南(下)、西(左)、北(上)、中五個(gè)區(qū)域放置,每個(gè)方向最多只能放置一個(gè)組件(或容器)。
GridLayout
形似一個(gè)無框線的表格,每個(gè)單元格中放一個(gè)組件
BoxLayout
就像整齊放置的一行或者一列盒子,每個(gè)盒子中一個(gè)組件

 

就上述的編輯器為例,如果選用 FlowLayout,那么兩個(gè)按鈕和一個(gè)多行文本框就會(huì)排列在一行——當(dāng)然這是窗口足夠?qū)挼那闆r;如果窗口稍窄一些,則可能分兩行排列,第一行有兩個(gè)按鈕,而第二行是多行文本框——這是最理想的情況;如果窗口再窄一些,就可能分三行排列,第一行和第二行分別放置一個(gè)按鈕,第三行放置多行文本框。因此,如果窗口大小可以改變,那么三個(gè)組件的位置關(guān)系也可能隨著窗口大小的變化而變化。其實(shí)上面所舉的例程中,大部分都是用的 FlowLayout,那是因?yàn)槲覀儧]有要求組件的布局。

如果選用 BorderLayout 的情況又如何呢?我們可以試著加入一個(gè)窗格 (JPanel,稍后講解),并將兩個(gè)按鈕放置在其中,然后將這個(gè)窗格加入到 BorderLayout 的北部 (即上部);再將多行文本框加入到 BorderLayout 中部。結(jié)果類似使用 FlowLayout 的第二種可能,是最理想的情況。而且,如果改變窗口大小,它們的位置關(guān)系仍然是北-中的關(guān)系,不會(huì)隨之改變。

剩下的兩種布局管理器,加以窗格 (JPanel) 的配合,也能夠很好的安排上述編輯器所需的三個(gè)組件。但是由于它們的使用稍為復(fù)雜一些,所以就不講了。下面就講講如何使用 FlowLayout 和 BorderLayout。

任何布局管理器,都需要用在容器上,比如 JFrame 的 Content Pane 和下面要說的 JPanel 都是容器(JFrame 默認(rèn)的 Content Pane 實(shí)際就是一個(gè) JPanel)。容器組件提供了一個(gè) setLayout 方法,就是用來改變其布局管理器的。默認(rèn)情況下,JFrame 的 Content Pane 使用的是 BorderLayout,而一個(gè)新產(chǎn)生的 JPanel 對(duì)象使用的是 FlowLayout。但不管怎樣,我們都可以調(diào)用它們的 setLayout 方法來改變其布局管理器。比如上述的編輯器中,我們要讓窗口 (JFrame 對(duì)象,假設(shè)為 frame) 使用 BorderLayout,就可以使用 frame.getContentPane().setLayout(new BorderLayout()); 來改變其布局管理器為一個(gè)新的 BorderLayout 對(duì)象。

然后,我們對(duì)布局管理器的直接操作就結(jié)束了,剩下的只需要往容器里添加組件。如果使用 FlowLayout,我們只需要使用容器的 add(Component c) 方法添加組件就行了。但是,如果使用 BorderLayout 就不一樣了,因?yàn)橐付ㄊ前呀M件添加到哪個(gè)區(qū)域啊。那我們就使用容器的 add(Component c, Object o) 方法添加組件,該方法的第二個(gè)參數(shù)就是指明添加到的區(qū)域用的。例如,上述編輯器中要添加一個(gè)多行文本框到 BorderLayout 的中部,就可以用 frame.getContentPane().add(new JTextArea(5, 15), BorderLayout.CENTER) 來實(shí)現(xiàn)。

BorderLayout 的五個(gè)區(qū)域分別是用下列五個(gè)常量來描述的:

BorderLayout.EAST
東(右)
BorderLayout.SOUTH
南(下)
BorderLayout.WEST
西(左)
BorderLayout.NORTH
北(上)
BorderLayout.CENTER

剛才已經(jīng)提到了使用 JPanel。JPanel 作為一個(gè)容器,可以包容一些組件,然后將這個(gè) JPanel 對(duì)象作為一個(gè)組件添加到另一個(gè)容器 (稱作父容器) 中。這個(gè)功能有什么好處呢?

上面不是提到 BorderLayout 的一個(gè)區(qū)域中只能添加一個(gè)組件嗎?但是我們的編輯器需要添加兩個(gè)按鈕到它的北部,怎么辦?下面的例子中,我們就會(huì)用的一個(gè) JPanel 包容了這兩個(gè)按鈕,然后再將這個(gè) JPanel 對(duì)象作為一個(gè)組件添加到設(shè)置布局管理器為 BorderLayout 的 Content Pane 中。

上面說到各布局管理器的布局特點(diǎn)的時(shí)候,幾乎每一種都是一個(gè)區(qū)域只能添加一個(gè)組件,那我們想添加多個(gè)組件到一個(gè)區(qū)域的時(shí)候,就要用到 JPanel 了。如果還沒有明白,稍后看一段程序可能更易于理解。

而滾動(dòng)窗格 (JScrollPane) 呢?它是一個(gè)能夠自己產(chǎn)生滾動(dòng)條的容器,通常只包容一個(gè)組件,并且根據(jù)這個(gè)組件的大小自動(dòng)產(chǎn)生滾動(dòng)條。比如上面講 JTextArea 的時(shí)候提到:JTextAera 會(huì)隨用戶輸入的內(nèi)容自動(dòng)擴(kuò)展大小,很容易打破各組件的布局。但是,如果我們將它包容在一個(gè)滾動(dòng)窗格中,它的擴(kuò)展就不會(huì)直接反映在大小的變化上,而會(huì)反映在滾動(dòng)窗格的滾動(dòng)條上,也就不會(huì)打破各組件的布局了。稍后的例子會(huì)讓你清清楚楚。

是不是等著看例子了?好,例子來了:

  1. /*  
  2.  * TestPanels.java  
  3.  * @author Fancy  
  4.  */ 
  5.  
  6. import java.awt.BorderLayout;  
  7.  
  8. import javax.swing.JButton;  
  9. import javax.swing.JFrame;  
  10. import javax.swing.JPanel;  
  11. import javax.swing.JScrollPane;  
  12. import javax.swing.JTextArea;  
  13.  
  14. public final class TestPanels extends JFrame {  
  15.  
  16.     public static void main(String[] args) {  
  17.         TestPanels tp = new TestPanels();  
  18.         tp.setVisible(true);  
  19.     }  
  20.  
  21.     public TestPanels() {  
  22.         setDefaultCloseOperation(EXIT_ON_CLOSE);  
  23.  
  24.         JPanel panel = new JPanel();  
  25.         for (int i = 0; i < 2; i++) {  
  26.             panel.add(new JButton("Button 00" + i));  
  27.         }  
  28.  
  29.         JTextArea textArea = new JTextArea(515);  
  30.         textArea.setLineWrap(true);  
  31.         JScrollPane scrollPane = new JScrollPane(textArea);  
  32.         getContentPane().add(panel, BorderLayout.NORTH);  
  33.         getContentPane().add(scrollPane, BorderLayout.CENTER);  
  34.  
  35.         pack();  
  36.     }  
  37.  

這個(gè)例子的運(yùn)行結(jié)果如下圖,正是我們想要的結(jié)果——上面兩個(gè)按鈕,下面是一個(gè)可以滾動(dòng)的多行文本框:

上例中首先產(chǎn)生了一個(gè) JPanel 對(duì)象 (JPanel panel = new JPanel();),然后將兩個(gè)按鈕置于其中 (panel.add ...);然后產(chǎn)生了一個(gè)多行文本框 (JTextArea textArea = new JTextArea(5, 15);),并使用一個(gè)滾動(dòng)窗格將它包裹起來 (JScrollPane scrollPane = new JScrollPane(textArea);),使之成為可以滾動(dòng)的多行文本框。最后將兩個(gè)容器 (JPanel 對(duì)象和 JScrollPane 對(duì)象) 分別添加到了窗口的北部 (getContentPane().add(panel, BorderLayout.NORTH);) 和中部 (也就是剩余部分,getContentPane().add(scrollPane, BorderLayout.CENTER);)。

好像有點(diǎn)不對(duì)勁,是什么呢?對(duì)了,我們沒有設(shè)置 Content Pane 的布局管理器為 BorderLayout 啊,為什么……剛才不是說了嗎,JFrame 的 Content Pane 的默認(rèn)布局管理器就是 BorderLayout,所以不用再設(shè)置了。

好了,《簡述 Java 圖形用戶界面設(shè)計(jì)》就告一段落了。由于篇幅有限,這里說的都是初級(jí)知識(shí),有此基礎(chǔ),設(shè)計(jì)復(fù)雜一點(diǎn)的圖形用戶界面也就不是難事了!

原文鏈接:http://blog.csdn.net/jamesfancy/article/details/1196585

【編輯推薦】

  1. Effective Java 創(chuàng)建和銷毀對(duì)象
  2. Java編碼及網(wǎng)絡(luò)傳輸中的編碼問題
  3. 探討:Java中刪除數(shù)組中重復(fù)元素
  4. 代碼分享:Swing外觀 抗鋸齒 字體設(shè)置
  5. Java的動(dòng)態(tài)綁定機(jī)制
責(zé)任編輯:林師授 來源: jamesfancy的博客
相關(guān)推薦

2012-01-16 16:16:49

JavaSwing

2011-06-01 10:58:57

2009-07-09 13:44:22

Swing動(dòng)態(tài)界面設(shè)計(jì)

2011-06-01 10:30:41

用戶界面

2011-04-22 11:01:36

框架布局界面設(shè)計(jì)Android

2011-05-19 08:49:01

使用片段界面設(shè)計(jì)Android

2011-04-11 17:07:33

布局基礎(chǔ)用戶界面設(shè)計(jì)Android

2011-04-08 13:58:10

Android界面設(shè)計(jì)

2011-04-11 17:25:30

線性布局用戶界面設(shè)計(jì)Android

2011-04-19 09:19:37

相對(duì)布局界面設(shè)計(jì)Android

2014-11-26 10:42:55

界面設(shè)計(jì)

2011-06-01 09:31:46

用戶界面設(shè)計(jì)

2013-11-27 10:12:11

2011-03-02 14:03:02

DashboardAndroid用戶界面反例模板

2011-03-02 10:49:42

DashboardAndroid用戶界面設(shè)計(jì)模板

2011-12-20 10:42:22

Android應(yīng)用界面設(shè)計(jì)

2014-04-03 09:49:20

設(shè)計(jì)界面設(shè)計(jì)

2012-04-28 10:25:11

JavaSwing

2013-12-26 15:46:30

Android開發(fā)Android應(yīng)用用戶界面設(shè)計(jì)

2012-04-24 09:40:42

SwingJava
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)