Java基礎知識筆記-11_2-Swing用戶界面組件html
這章教程兩個版本,一個語法是非lambda表達式版本,另外一個是lambda表達式版本java
非lambda表達式版本
1 Java Swing概述
Java的java.awt包,即抽象窗口工具包JDK1.2推出後,增長了一個新的javax.swing包,該包提供了更爲強大的GUI類,這兩個包中一部分類的層次關係的UML類以下圖所示程序員
Component類的部分子類編程
學習GUI編程時,必須很好的瞭解兩個概念:容器類(Container)和組建類(Component)。javax.swing包中JComponent類是java.awt包中Container類的一個直接子類,是Component類的一個間接子類,學習GUI編程主要是學習掌握使用Component類的一些重要的子類。windows
如下是GUI編程的一些基本知識點:設計模式
- Java能夠把component類的子類或間接自類建立的對象稱爲一個組件。
- Java能夠把container的子類或間接子類建立的對象稱爲一個容器。
- 能夠向容器中添加組件。container類提供了一個public方法
add()
,一個容器能夠調用這個方法將組建添加到該容器中。 - 容器調用
removeAll()
方法能夠以掉容器中的所有組件,調用remove(Component c)
方法能夠移掉容器中參數C指定的組件。 - 注意到容器自己也是一個組件,所以能夠把一個容器添加到另外一個容器中實現容器的嵌套。
2 窗口
一個基於GUI的應用程序應當提供一個能和操做系統直接交互的容器,該容器能夠被直接顯示、繪製在操做系統控制的平臺上,例如顯示器上,這樣的容器被稱做GUI設計中的底層容器。數組
Java提供的JFrame類的實例就是一個底層容器(JDialog類的實例也是一個底層容器,見後面的11.6節),即一般所說的窗口。其餘組件必須被添加到底層容器中,以便藉助這個底層容器和操做系統進行信息交互。簡單地講,若是應用程序須要一個按鈕,並但願用戶和按鈕交互,即用戶單擊按鈕使程序作出某種相應的操做,那麼這個按鈕必須出如今底層容器中,不然用戶沒法看見按鈕,更沒法讓用戶和按鈕交互。瀏覽器
JFrame類是Container類的間接子類。當須要一個窗口時,可以使用JFrame或其子類建立個對象。 窗口也是個容器,能夠向窗口添加組件。須要注意的是,窗口默認地被系統添加到顯示器屏幕上,所以不容許將一個窗口添加到另外一個容器中。安全
2.1 JFrame經常使用方法
JFrame()
: 建立一一個無標題的窗口。JFrame(Strings)
: 建立標題爲s的窗口。public void setisble(boolean b)
:設置窗口是否可見。窗口默認是不可見的。public void dispose()
:撒銷當前窗口,並釋放當前窗口所使用的資源。public void setDefaultCloseOperation(int operation)
:該方法用來設置單機窗體右上角的關閉圖標後,程序會作出怎樣的處理,其中的參數operation取JFrame類中的下列int型static常量,程序根據參數operation取值作出不一樣的處理。HIDE_ON_CLOSE
:什麼也不作DISPOSE_ON_CLOSE
:隱藏當前窗口,並釋放窗體佔有的其餘資源。EXIT_ON_CLOSE
:結束窗口所在的應用程序
import javax.swing.*; import java.awt.*; public class exercise { public static void main(String args[]) { JFrame windows1=new JFrame("The first windows"); JFrame windows2=new JFrame("The second windows"); Container con=windows1.getContentPane(); con.setBackground(Color.yellow); windows1.setBounds(60,100,188,108); windows2.setBounds(260,00,188,108); windows1.setVisible(true); windows1.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); windows2.setVisible(true); windows2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } }
注意兩次單擊關閉窗口,程序運行效果不一樣。網絡
2.2 菜單條,菜單,菜單項
窗口中的菜單項放在菜單裏,菜單放在菜單條裏。
1.菜單條
JComponent類的子類JMenubar
負責建立菜單條,即JMenubar的一個實例就是一個菜單條,JFrame類有一個將菜單條放置到窗口的方法;
setJMenuBar(JMenuBar bar);
該方法將菜單條添加到窗口的頂端,須要注意的是,只能向窗口中添加一個菜單條。
2.菜單
JComponent類的子類JMenu
負責建立菜單,即JMenu的一個實例就是一個菜單。
3.菜單項
JComponent類的子類JMenuItem
負責建立菜單項,即JMenuItem的一個實例就是一個菜單項。
4.嵌入子菜單
JMenu是JMenuItem
的子類,所以菜單自己也是一個菜單項,當把一個菜單看做菜單項添加到某個菜單中,稱這樣的菜單爲子菜單
5.菜單上的圖標
咱們先用Icon聲明一個圖標,而後使用其子類ImageIcon類建立一個圖標,如:
Icon icon=new ImageIcon("a.gif");
而後菜單項調用setlcon(Icon icon)
方法將圖標設置爲icon.
例11.2中在主類Examplell_2的main 方法中,用Frame的子類WindowMenu建立一個含有菜單的窗口,效果如圖11.3所示。
import javax.swing.*; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import static javax.swing.JFrame.*; class WindowMenu extends JFrame { JMenuBar menubar; JMenu menu,subMenu; JMenuItem item1, item2; public WindowMenu(){ } public WindowMenu(String s,int x,int y,int w,int h) { init(s); setLocation(x,y); setSize(w,h); setVisible(true); setDefaultCloseOperation(DISPOSE_ON_CLOSE); } void init(String s){ setTitle(s); menubar=new JMenuBar(); menu=new JMenu("菜單"); subMenu=new JMenu("軟件項目"); item1=new JMenuItem("java話題",new ImageIcon("a.gif")); item2=new JMenuItem("動畫話題",new ImageIcon("b.gif")); item1.setAccelerator(KeyStroke.getKeyStroke('A')); item2.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,InputEvent.CTRL_MASK)); menu.add(item1); menu.addSeparator(); menu.add(item2); menu.add(subMenu);//把sunmenu做爲menu的一個菜單項 subMenu.add(new JMenuItem("汽車銷售系統",new ImageIcon("c.gif"))); subMenu.add(new JMenuItem("農場管理系統",new ImageIcon("d.gif"))); menubar.add(menu); setJMenuBar(menubar); } } public class exercise { public static void main(String args[]) { WindowMenu win=new WindowMenu("帶菜單的窗口",20, 30,200,190); } }
3 經常使用組件與佈局
可使用JComponent的子類建立各類組件,利用組件能夠完成應用程序與用戶的交互。
3.1 經常使用組件
1.文本框
使用JComponent的子類JTestField
建立文本框,容許用戶在文本框輸入單行文本。
2.文本區
使用JComponent的子類JButton
類建立按鈕,容許用戶單擊按鈕
4.標籤
使用JComponent的子類JLabel
類建立標籤,容許用戶單擊按鈕。
5.選擇框
使用JComponent的子類JCheckBox
類建立選擇框,爲用戶提供多種選擇。選擇框有兩種狀態,一種是選中,另外一種是未選中,用戶經過單該組件切換狀態。
6.單選按鈕
使用JComponent的子類JRadioButton
類創單項選擇框,爲用戶提供單項選擇。
7.下拉列表
使用JComponent的子類JComboBox
類建立下拉列表,爲用戶提供單項選擇,用戶能夠在下拉列表看到第一個選項和他旁邊的箭頭按鈕,當用戶單擊按鈕箭頭時,選項列表打開。
8.密碼框
可使用JComponent的子類JPasswordField
建立密碼框,密碼框可使用
setEchoChar(char c)
方法從新設置回顯字符,用戶輸入密碼時,密碼框只顯示回顯字符。
密碼框調用
char[]getPassword()
方法能夠返回實際的密碼。
import java.awt.*; import javax.swing.*; class ComponentInWindow extends JFrame { JTextField text; JButton button; JCheckBox checkBox1,checkBox2,checkBox3; JRadioButton radio1,radio2; ButtonGroup group; JComboBox comBox; JTextArea area; public ComponentInWindow() { init(); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } void init() { setLayout((new FlowLayout())); add(new JLabel("文本框")); text=new JTextField(10); add(text); add(new JLabel("按鈕")); button=new JButton("肯定"); add(button); add(new JLabel("選擇框")); checkBox1=new JCheckBox("喜歡音樂"); checkBox2=new JCheckBox("Like basketball"); checkBox3=new JCheckBox("like travel"); add(checkBox1); add(checkBox2); add(checkBox3); add(new JLabel("單選按鈕")); group=new ButtonGroup(); radio1=new JRadioButton("男"); radio2=new JRadioButton("女"); group.add(radio1); group.add(radio2); add(new JLabel("下拉列表")); comBox=new JComboBox<>(); comBox.addItem("選項1"); comBox.addItem("選項2"); comBox.addItem("選項3"); add(comBox); add((new JLabel("文本區"))); area=new JTextArea(6,12); add(new JScrollPane(area)); } } public class exercise{ public static void main(String args[]) { ComponentInWindow win=new ComponentInWindow(); win.setBounds(100,100,310,260); win.setTitle("經常使用組件"); } }
3.2 經常使用容器
JComponent是Container的子類,所以JComponent子類建立的組建也都是容器,可是咱們不多將JButton,JTeseField,JCheckBox
等組件當容器來使用。JComponent專門提供了一些常常用來添加組件的容器,相對於JFrame底層容器,本節提到的容器習慣性被稱爲中間容器,中間容器必須被添加到底層容器中才會發生做用
1.JPanel面板
咱們常常會使用JPlanel建立一個面板,而後再向這個面板添加組件,而後把這個面板添加到其餘的容器中。JPlanel面板的默認佈局是FlowLayout佈局。
2.滾動窗格JSrollPane
滾動窗格只能夠添加一個組件,能夠把一個組件放到一個滾動窗格中,而後經過滾動條來操做該組件,JTextArea不自帶滾動條,所以咱們須要把文本區放入到一個滾動窗格中,例如:
JScrollPane scroll=new JScrollPane(new JTextArea());
3.拆分窗格ISplitPane
顧名思義,拆分窗格就是被分紅兩部分的容器,拆分窗格有兩種類型:水平拆分和垂直拆分。水平拆分窗格用一條拆分線把窗格分紅左右兩部分,左面放一個組件,右面放一組件,拆分線能夠水平移動。垂直拆分窗格用一條拆分線把窗格分紅上下兩部分,上面放個組件,下面放一個組件,拆分線能夠垂直移動
JSplitPane的兩個經常使用的構造方法:
JSplitPane(int a, Component b, Component c);
參數a取JSplitPane的靜態常量HORIZONTAL_SPLIT或VERTICAL_SPLIT,以解決水平仍是垂直拆分,後兩個參數決定要放置的組件,當拆分線移動時,組件不是連續變化的
JSplitPane(int a, boolean b, Component c, Component d);
參數a取JSplitPane的靜態常量HORIZONTAL_SPLIT或VERTICAL_SPLIT,以決定是水平仍是垂直拆分。參數b決定當拆分線移動時,組件是否連續變化(true是連續),後兩個參數決定要放置的組件, SPlit Pane拆分窗格還能夠調用setDividerlocation(int)方法修改拆分線的初始位置
4 JLayeredPane分層窗格
若是添加到容器中的組件常常須要處理重疊問題,就能夠考慮將組件添加到分層窗格分層窗格分紅5個層,分層窗格使用
add(Component com, int layer);
添加組件com,並指定com所在的層,其中參數layer取值JLayeredPane類中的類常量
DEFAULT_LAYER、 PALETTE_LAYER、 MODAL_LAYER、 POPUP_LAYES、 DRAG_LAYER
DEFAULT LAYER是最底層,添加到DEFAULT_LAYER層的組件若是和其餘層的組件發生重疊時,將被其餘組件遮擋。 DRAG_LAYER層是最上面的層,若是分層窗格中添加了許多組件,當用戶用鼠標移動一個組件時,能夠把該組件放到DRAG__LAYER層
這樣,用戶在移動組件過程當中,該組件就不會被其餘組件遮擋。添加到同一層上的組件,如發生重疊,後添加的會遮擋先添加的組件。分層窗格調用
public void setLayer(Component c, int layer);
能夠從新設置組件c所在的層,調用
public int getlayer( Component c)
能夠獲取組件c所在的層數
3.3經常使用佈局
當把組件添加到容器中時,但願控制組件在容器中的位置,這就須要學習設計的知識。本節將分別介紹java.awt包中的FlowLayout
BorderLayout
Cardlayout
GridLayout
佈局
容器可使用方法setLayout(佈局對象);
設置本身的佈局
1.FlowLayout佈局
FlowLayout類建立的對象稱做FlowLayout型佈局。FlowLayout型佈局是JPanel容器的默認佈局,即JPanel及其子類建立的容器對象,若是不專門爲其指定佈局,則它們的佈局就是Flow Layout型佈局
Flow Layout類的一個經常使用構造方法以下,該構造方法能夠建立一個居中對齊的佈局對象。例如:
FlowLayout flow=new FlowLayout();
若是一個容器con使用這個佈局對象
con.setLayout(flow);
那麼,con可使用Container類提供的add方法將組件順序地添加到容器中,組件按照加入的前後順序從左向右排列,一行排滿以後就轉到下一行繼續從左至右排列,每一行中的組件都居中排列,組件之間的默認水平和垂直間隙是5個像素,組件的大小爲默認的最住大小,例如,按鈕的大小恰好能保證顯示其上面的名字。對於添加到使用Flowlayout佈局的容器中的組件,組件調用setSize(int x,int y);
設置的大小無效,若是須要改變最佳大小,組件需調用
public void setPreferredsize(Dinension preferredsize);
設置大小,例如:
button.setPreferredsize(new Dinension(20,20));
FlowLayout佈局對象調用
setAliginmen(int aligin);
方法能夠從新設置佈局的對齊方式,其中aligin能夠取值ElowLayout.LEFT,FlowLayout.CENTER,FlowLayout.RIGHT
FlowLayout佈局對象調用setHgap(int hgap)
方法和setVgap(intvgap)
能夠從新設置水平間隙和垂直間隙。
2.BorderLayout佈局
Borderlayout佈局是window型容器的默認佈局,例如JFrame、JDialog都是Window類的子類,它們的默認佈局都是BorderLayout佈局,Borderlayout也是一種簡單的佈局策略,若是一個容器使用這種佈局,那麼容器空間簡單地劃分爲東、西、南、北、中5個區域,中間的區域最大。每加入一個組件都應該指明把這個組件加在哪一個區域中,區域由Borderlayout中的靜態常量CENTER、NORTH、SOUTH、WEST、EAST表示,例如,一個使用BorderLayout佈局的容器con,可使用add方法將一個組件b添加到中心區域
con.add(b,BorderLayout.CENTER); 或者: on.add(BorderLayour.CENTER,b);
添加到某個區域的組件將佔據整個這個區域,每一個區域只能放置一個組件,若是向某個已放置了組件的區域再放置一個組件,那麼先前的組件將被後者替換掉,使用BorderLayout佈局的容器最多能添加5個組件,若是容器中須要加入超過5個組件,就必須使用容器的嵌套或改用其餘的佈局策略.
3.CardLayout佈局
使用CardLayout的容器能夠容納多個組件,這些組件被層疊放入容器中,最早加入容器的是第一張(在最上面),依次向下排序,使用該佈局的特色是,同一時刻容器只能從這些件中選出一個來顯示,就像疊「撲克牌」,每次只能顯示其中的一張,這個被顯示的組件將佔據全部的容器空間
假設有一個容器con,那麼,使用Cardlayout的通常步驟以下
- 建立CardLayout對象做爲佈局,如
Cardlayout card=new Cardlayout();
- 使用容器的setlayout()方法爲容器設置佈局,如
con.setLayout(card);
- 容器調用
add(String s,Component b)
將組件b加入容器,並給出了顯示該組件的代號s。組件的代號是一個字符串,和組件的名沒有必然聯繫,可是,不一樣的組件代號必須互不相同。最早加入con的是第一張,依次排序. - 建立的佈局card用Cardlayout類提供的
show()
方法,顯示容器con中組件代號爲s的組件card.show(con,s)
;
也能夠按組件加入容器的順序顯示組件:
card.first(con)
顯示con中的第一個組件;
card.last(con)
顯示con中最後一個組件;
card.next(con)
顯示當前正在被顯示的組件的下個組件;
card.previous(con)
顯示當前正在被顯示的組件的前一個組件
4.GridLayout佈局
GridLayout是使用較多的佈局編輯器,其基本佈局策略是把容器劃分紅若干行乘若干列的網格區域,組件就位於這些劃分出來的小格中, Gridlayout比較靈活,劃分多少網格由程序自由控制,並且組件定位也比較精確,使用GridLayout佈局編輯器的通常步驟以下
使用 GridLayout的構造方法
GridLayout(int m,int n);
建立佈局對象,指定劃分網格 的行數m和列數n,例如
Gridlayout grid= new Gridlayout(10,8);
使用 GridLayout佈局的容器調用方法add(Component c)
將組件c加入容器,組件進入容器的順序將按照第一行第一個、第一行第二個行最後一個、第二行第一個、…、最後一行第一個、…、最後一行最後一個.
使用GridLayout佈局的容器最多可添加m×n個組件,GridLayout佈局中每一個網格都是相同大小而且強制組件與網格的大小相同.
因爲Gridlayout佈局中每一個網格都是相同大小而且強制組件與網格的大小相同,使得容器中的每一個組件也都是相同的大小,顯得很不天然。爲了克服這個缺點,你可使用容器套,如,一個容器使用GridLayout佈局,將容器分爲三行一列的網格,那麼你能夠把另個容器添加到某個網格中,而添加的這個容器又能夠設置爲GridLayout佈局、FlowLayout佈局、CarderLayout佈局或BorderLayout佈局等,利用這種嵌套方法,能夠設計出符合定須要的佈局.
3.4 選項卡窗格
JTabbedPane建立的對象也是一個容器,因爲JTabbedPane在設計GUl程序時比較方便實用,因此單獨列出一小節來說解。
JTabbedPane建立的對象稱爲選項卡窗格。JTabbedPane窗格的默認佈局是CardLayour佈局,而且自帶一些選項卡(不需用戶添加),這些選項卡與用戶添加到JTabbedPane窗格中的組件相對應,也就是說,當用戶向JTabbedPane窗格添加一個組件時,JTabbedPane窗格就會自動指定給該組件一個選項卡,單擊該選項卡,JTabbedPane窗格將顯示對應的組件,選項卡窗格自帶的選項卡默認地在該選項卡窗格的頂部,從左向右依次排列,選項卡的順序和對應的組件的順序相同。
JTabbledPane窗格可使用
add(String text,Component c);
方法將組件c添加到JTabbedPane窗格中,並指定和組件c對應的選項卡的文本提示text,使用 JTabbedPane窗格的構造方法
public JTabbedPane(int tabPlacenent);
建立的選項卡窗格的選項卡的位置由參數tabPlacement指定,該參數的有效值爲JTabbedPane.TOP
, JTabbedPane.BOTTOM
, JTabbedPane.LEFT
和JTablePane.RIGHT
例11.4的 Example14窗口中有一個選項卡窗格,選項卡窗格中又添加了3個不一樣佈局的面板FlowLayoutJPanel,BorderlayoutJPanel和GridLayoutJPanel並設置了相對應的選項卡的文本提示.
import javax.swing.*; import java.awt.*; class FlowLayoutJPanel extends JPanel{//JPlane型容器默認佈局 FlowLayoutJPanel(){ add(new JLabel("FlowLayout佈局的面板")); add(new JButton(new ImageIcon("dog.jpg"))); add(new JScrollPane(new JTextArea(12,15))); } } class GridLayoutJPlanel extends JPanel {//使用較多的佈局方式 public GridLayoutJPlanel() { GridLayout gird=new GridLayout(12,12); setLayout(gird); Label label[][]=new Label[12][12]; for(int i=0;i<12;i++) { for(int j=0;j<12;j++) { label[i][j]=new Label(); if((i+j)%2==0) { label[i][j].setBackground(Color.black); } else label[i][j].setBackground(Color.white); add(label[i][j]); } } } } class BorderLayoutJPanel extends JPanel{//Windows默認佈局 JButton bSouth,bNorth,bEast,bWest; JTextArea bCenter; public BorderLayoutJPanel() { setLayout(new BorderLayout());// TODO Auto-generated constructor stub bSouth=new JButton("南"); bNorth=new JButton("北"); bEast=new JButton("東"); bWest=new JButton("西"); bCenter=new JTextArea("中心"); add(bNorth, BorderLayout.NORTH); add(bSouth, BorderLayout.SOUTH); add(bEast, BorderLayout.EAST); add(bWest, BorderLayout.WEST); add(bCenter, BorderLayout.CENTER); validate(); } } public class exercise extends JFrame{ JTabbedPane p; public exercise(){ setBounds(100,100,500,300); setVisible(true); p=new JTabbedPane(JTabbedPane.LEFT); p.add("觀看FlowLayout", new FlowLayoutJPanel()); p.add("觀看GrilLayot", new GridLayoutJPlanel()); p.add("觀看BorderLayout", new BorderLayoutJPanel()); p.validate();//確保組件具備有效的佈局 add(p,BorderLayout.CENTER); validate();//確保組件具備有效的佈局 setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); } public static void main(String args[]) { new exercise(); } }
lambda表達式版本
上一章主要介紹瞭如何使用Java中的事件模式。經過學習讀者已經初步知道了構造圖形用戶界面的基本方法。本章將介紹構造功能更加齊全的圖形用戶界面(GUI) 所須要的一些重要工具。下面,首先介紹Swing的基本體系結構。要想弄清如何有效地使用一些更高級的組件,必須瞭解底層的東西。而後,再講述Swing中各類經常使用的用戶界面組件,如文本框、單選按鈕以及菜單等。接下來,介紹在不考慮特定的用戶界面觀感時,如何使用Java中的佈局管理 器排列在窗口中的這些組件。最後,介紹如何在Swing中實現對話框。本章囊括了基本的Swing組件,如文本組件、按鈕和滑塊等,這些都是基本的用戶界面組件,使用十分頻繁。Swing中的高級組件將在卷Ⅱ中討論。
1 Swing 和模型-視圖-控制器設計模式
前面說過,本章將從Swing組件的體系結構開始。首先,咱們討論設計模式的概念,而後再看一下Swing框架中最具影響力的「模型-視圖-控制器」模式。
1.1 設計模式
在「模型-視圖-控制器」模式中,背景是顯示信息和接收用戶輸入的用戶界面系統。有關「模型-視圖-控制器」模式將在接下來的章節中講述。這裏有幾個衝突因素。對於同一數據來講,可能須要同時更新多個可視化表示。例如,爲了適應各類觀感標準,可能須要改變可視化表示形式;又例如,爲了支持語音命令,可能須要改變交互機制。解決方案是將這些功能分佈到三個獨立的交互組件:模型、視圖和控制器。模型-視圖-控制器模式並非AWT和Swing設計中使用的惟一模式。下列是應用的另外幾種模式:
- 容器和組件是「組合(composite)」 模式
- 帶滾動條的面板是「 裝飾器(decorator)」 模式
- 佈局管理器是「策略(strategy)」 模式
設計模式的另一個最重要的特色是它們已經成爲文化的一部分。只要談論起模型-視圖-控制器或「裝飾器」模式,遍佈世界各地的程序員就會明白。所以,模式已經成爲探討設計方案的一種有效方法。
1.2 模型-視圖-控制器模式
讓咱們稍稍停頓一下子,回想一下構成用戶界面組件的各個組成部分,例如,按鈕、複選框、文本框或者複雜的樹形組件等。每一個組件都有三個要素:
- 內容,如:按鈕的狀態(是否按下),或者文本框的文本。
- 外觀(顏色,大小等)。
- 行爲(對事件的反應)。
這三個要素之間的關係是至關複雜的,即便對於最簡單的組件(如:按鈕)來講也是如此。很明顯,按鈕的外觀顯示取決於它的觀感。Metal按鈕的外觀與Windows按鈕或者Motif按鈕的外觀就不同。另外,外觀顯示還要取決於按鈕的狀態:當按鈕被按下時,按鈕須要被從新繪製成另外一種不一樣的外觀。而狀態取決於按鈕接收到的事件。當用戶在按鈕上點擊時,按鈕就被按下。
固然,在程序中使用按鈕時,只須要簡單地把它當作是一個按鈕,而不須要考慮它的內部工做和特性。畢竟,這些是實現按鈕的程序員的工做。不管怎樣,實現按鈕的程序員就要對這些按鈕考慮得細緻一些了。畢竟,不管觀感如何,他們必須實現這些按鈕和其餘用戶界 面組件,以便讓這些組件正常地工做。爲了實現這樣的需求,Swing設計者採用了一種頗有名的設計模式(design pattern):模型-視圖-控制器(model-view-controller)模式。這種設計模式同其餘許多設計模式同樣,都遵循第5章介紹過的面向對象設計中的一個基本原則: 限制一個對象擁有的功能數量。不要用一個按鈕類完成全部的事情,而是應該讓一個對象負責組件的觀感,另外一個對象負責存儲內容。模型-視圖-控制器(MVC) 模式告訴咱們如何實現這種設計,實現三個獨立的類:
- 模型(model): 存儲內容。
- 視圖(view): 顯示內容。
- 控制器(controller): 處理用戶輸入。
這個模式明確地規定了三個對象如何進行交互。模型存儲內容,它沒有用戶界面。按鈕的內容很是簡單,只有幾個用來表示當前按鈕是否按下,是否處於活動狀態的標誌等。文本框內容稍稍複雜一些,它是保存當前文本的字符串對象。這與視圖顯示的內容並不一致---若是內容的長度大於文本框的顯示長度,用戶就只能看到文本框能夠顯示的那一部分
模型必須實現改變內容和查找內容的方法。例如,一個文本模型中的方法有:在當前文本中添加或者刪除字符以及把當前文本做爲一個字符串返回等。記住:模型是徹底不可見的。顯示存儲在模型中的數據是視圖的工做。
註釋:「模式」這個術語可能不太貼切,由於人們一般把模式視爲一個抽象概念的具體表示。汽車和飛機的設計者構造模式來模擬真實的汽車和飛機。但這種類比可能會使你對模型-視圖-控制器模式產生錯誤的理解。在設計模式中,模型存儲完整的內容,視圖給出了內容的可視化顯示(完整或者不完整)。一個更恰當的比喻應當是模特爲畫家擺好姿式。
此時,就要看畫家如何看待模特,並由此來畫一張畫了。那張畫是一幅規矩的肖像畫,或是一幅印象派做品,仍是一幅立體派做品(以古怪的曲線來描繪四肢)徹底取決於畫家。
模型-視圖-控制器模式的一個優勢是一個模型能夠有多個視圖,其中每一個視圖能夠顯示所有內容的不一樣部分或不一樣形式。 例如,一個HTML編輯器經常爲同一內容在同一時刻提供兩個視圖:一個WYSIWYG (所見即所得)視圖和一個「原始標記」 視圖(見圖 12-3 )。當經過某一個視圖的控制器對模型進行更新時,模式會把這種改變通知給兩個視圖。視圖獲得通知之後就會自動地刷新。固然,對於一個簡單的用戶界面組件來講,如按鈕,不須要爲同一模型提供多個視圖。
控制器負責處理用戶輸入事件,如點擊鼠標和敲擊鍵盤。而後決定是否把這些事件轉化成對模型或視圖的改變。例如,若是用戶在一個文本框中按下了一個字符鍵,控制器調用模型中的「插入字符」命令,而後模型告訴視圖進行更新,而視圖永遠不會知道文本爲何改變了。可是若是用戶按下了一個光標鍵,那麼控制器會通知視圖進行卷屏。捲動視圖對實際文本不會有任何影響,所以模型永遠不會知道這個事件的發生。
在程序員使用Swing組件時,一般不須要考慮模型-視圖-控制器體系結構。每一個用戶界面元素都有一個包裝器類(如JButton或JTextField) 來保存模型和視圖。當須要查詢內容 (如文本域中的文本)時, 包裝器類會向模型詢問而且返回所要的結果。當想改變視圖時(例如, 在一個文本域中移動光標位置), 包裝器類會把此請求轉發給視圖。然而,有時候包裝器轉發命令並不得力。在這種狀況下,就必須直接地與模型打交道(沒必要直接操做視圖--這是觀感代碼的任務)。
除了「本職工做」外,模型-視圖-控制器模式吸引Swing設計者的主要緣由是這種模式容許實現可插觀感。每一個按鈕或者文本域的模型是獨立於觀感的。固然可視化表示徹底依賴於特殊觀感的用戶界面設計,且控制器能夠改變它。例如,在一個語音控制設備中,控制器須要處理的各類事件與使用鍵盤和鼠標的標準計算機徹底不一樣。經過把底層模型與用戶界面分離開,Swing設計者就可以重用模型的代碼,甚至在程序運行時對觀感進行切換。
固然,模式只能做爲一種指導性的建議而並無嚴格的戒律。沒有一種模式可以適用於全部狀況。例如,使用「窗戶位置」模式(設計模式中並不是主要成分)來安排小臥室就不太合適。一樣地,Swing設計者發現對於可插觀感實現來講,使用模型-視圖-控制器模式並不是都是完美的。模型容易分離開,每一個用戶界面組件都有一個模型類。可是,視圖和控制器的職責分工有時就不很明顯,這樣將會致使產生不少不一樣的類。固然,做爲這些類的使用者來講,沒必要爲這些細節費心。前面已經說過,這些類的使用者根本無需爲模型操心,僅使用組件包裝器類便可。
1.3 Swing 按鈕的模型-視圖-控制器分析
前一章已經介紹瞭如何使用按鈕,當時沒有考慮模型、視圖和控制器。按鈕是最簡單的用戶界面元素,因此咱們從按鈕開始學習模型-視圖-控制器模式會感受容易些。對於更復雜的Swing組件來講,所遇到的類和接口都是相似的。
對於大多數組件來講,模型類將實現一個名字以Model結尾的接口,例如,按鈕就實現了ButtonModel接口。實現了此接口的類能夠定義各類按鈕的狀態。實際上,按鈕並不複雜,在Swing庫中有一個名爲DefaultButtonModel的類就實現了這個接口。
讀者能夠經過查看ButtonModd接口中的特徵來了解按鈕模型所維護的數據類別。表12-1列出了這些特徵。
屬性名 | 值 |
---|---|
actionCommand | 與按鈕關聯的動做命令字符串 |
mnemonic | 按鈕的快捷鍵 |
armed | 若是按鈕被按下且鼠標仍在按鈕上則爲true |
enable | 若是按鈕是可選擇的則爲true |
pressed | 若是按鈕被按下且鼠標按鍵沒有釋放則爲true |
rollover | 若是鼠標在按鈕之上則爲true |
selected | 若是按鈕已經被選擇(用於複選框和單選鈕) 則爲true |
每一個JButton對象都存儲了一個按鈕模型對象,能夠用下列方式獲得它的引用。
JButton button = new JButton("Blue"); ButtonModel model = button.getModel();
實際上,沒必要關注按鈕狀態的零散信息,只有繪製它的視圖纔對此感興趣。諸如按鈕是否可用這樣的重要信息徹底能夠經過JButton類獲得(固然,JButton類也經過向它的模型詢問來得到這些信息)。
下面查看ButtonModel接口中不包含的信息。模型不存儲按鈕標籤或者圖標。對於一個按鈕來講,僅憑模型沒法知道它的外觀(實際上,在有關單選鈕的12.4.2節中將會看到,這種純粹的設計會給程序員帶來一些麻煩)。
須要注意的是,一樣的模型(即DefaultButtonModel) 可用於下壓按鈕、單選按鈕、複選框、甚至是菜單項。固然,這些按鈕都有各自不一樣的視圖和控制器。當使用Metal觀感時,JButton類用 BasicButtonUI類做爲其視圖;用ButtonUIListener類做爲其控制器。一般,每 個 Swing組件都有一個相關的後綴爲UI的視圖對象,但並非全部的Swing組件都有專門的控制器對象。在閱讀JButton底層工做的簡介以後可能會想到:JButton到底是什麼?事實上,它僅僅是一個繼承了JComponent的包裝器類,JComponent包含了一個DefauUButtonModel對象,一些視圖數據(例如按鈕標籤和圖標)和一個負責按鈕視圖的BasicButtonUI對象。
2 佈局管理概述
2.1 邊框佈局
2.2 網格佈局
3 文本輸入
如今終於能夠開始介紹Swing用戶界面組件了。首先,介紹具備用戶輸入和編輯文本功能的組件。文本域(JTextField)和文本區(JTextArea)組件用於獲取文本輸入。文本域只能接收單行文本的輸入,而文本區可以接收多行文本的輸入。JPassword也只能接收單行文本的輸入,但不會將輸入的內容顯示出來。
這三個類都繼承於JTextComponent類。因爲JTextComponent是一個抽象類,因此不可以構造這個類的對象。另外,在Java中常會看到這種狀況。在査看API文檔時,發現本身正在尋找的方法實際上來自父類JTextComponent, 而不是來自派生類自身。例如,在一個文本域和文本區內獲取(get)、設置(set) 文本的方法實際上都是JTextComponent類中的方法
javax.swing.text.JTextComponent 1.2
String getText(); void setText(String text); //獲取或設置文本組件中的文本。 boolean isEditable(); void setEditabe(boolean b); //獲取或設置 editable 特性,這個特性決定了用戶是否能夠編輯文本組件中的內容。
3.1 文本域
把文本域添加到窗口的經常使用辦法是將它添加到面板或者其餘容器中,這與添加按鈕徹底同樣:
JPanel panel = new JPanel(); JTextField textField = new JTextField("Default input", 20); panel.add(textField);
這段代碼將添加一個文本域,同時經過傳遞字符串「Default input」進行初始化。構造器的第二個參數設置了文本域的寬度。在這個示例中,寬度值爲 20「列」。可是,這裏所說的列不是一個精確的測量單位。一列就是在當前使用的字體下一個字符的寬度。若是但願文本域最多可以輸入n個字符,就應該把寬度設置爲n列。在實際中,這樣作效果並不理想,應該將最大輸入長度再多設1~2個字符。列數只是給AWT設定首選(preferred)大小的一 個提示。若是佈局管理器須要縮放這個文本域,它會調整文本域的大小。在JTextField的構造器中設定的寬度並非用戶能輸入的字符個數的上限。用戶能夠輸入一個更長的字符串,可是當文本長度超過文本域長度時輸入就會滾動。用戶一般不喜歡滾動文本域,所以應該盡 量把文本域設置的寬一些。若是須要在運行時從新設置列數,可使用setColumns方法。
提示:使用setColumns方法改變了一個文本域的大小以後,須要調用包含這個文本框的容器的revalidate方法。
textField.setColumns(10); panel.revalidate();revalidate方法會從新計算容器內全部組件的大小,而且對它們從新進行佈局。調用revalidate方法之後, 佈局管理器會從新設置容器的大小,而後就能夠看到改變尺寸後的 文本域了。revalidate方法是JComponent類中的方法。它並非立刻就改變組件大小,而是給這個組件加一個須要改變大小的標記。這樣就避免了多個組件改變大小時帶來的重複計算。可是,若是想從新計算一個JFrame中的全部組件,就必須調用validate方法JFrame沒有擴展JComponent。
一般狀況下,但願用戶在文本域中鍵入文本(或者編輯已經存在的文本)。文本域通常初始爲空白。只要不爲JTextField構造器提供字符串參數,就能夠構造一個空白文本域:
JTextField textField = new JTextField(20);
能夠在任什麼時候候調用setText方法改變文本域中的內容。這個方法是從前面提到的JTextComponent中繼承而來的。例如:
textField.setText("Hello!");
而且,在前面已經提到,能夠調用getText方法來獲取用戶鍵入的文本。這個方法返回用戶輸入的文本。 若是想要將getText方法返回的文本域中的內容的先後空格去掉,就應該調用trim方法:
String text = textField.getText().trim;
若是想要改變顯示文本的字體,就調用setFont方法。
javax.swing.JTextField 1.2
JTextField(int cols); //構造一個給定列數的空 JTextField對象。 JTextField(String text, int cols); //構造一個給定列數、給定初始字符串的JTextField對象。 int getColumns(); void setColumns(int cols); //獲取或設置文本域使用的列數。javax.swing.JComponent 1.2
void revalidate(); //從新計算組件的位置和大小。 void setFont(Font f); //設置組件的字體。java.awt.Component 1.0
void validate(); //從新計算組件的位置和大小。若是組件是容器,容器中包含的全部組件的位置和大小 也被從新計算。 Font getFont(); //獲取組件的字體。
3.2 標籤和標籤組件
標籤是容納文本的組件,它們沒有任何的修飾(例如沒有邊緣),也不能響應用戶輸入。能夠利用標籤標識組件。例如:與按鈕不一樣,文本域沒有標識它們的標籤,要想用標識符標識這種不帶標籤的組件,應該
- 1 ) 用相應的文本構造一個JLabel組件。
- 2 ) 將標籤組件放置在距離須要標識的組件足夠近的地方, 以便用戶能夠知道標籤所標 識的組件。
JLabel的構造器容許指定初始文本和圖標,也能夠選擇內容的排列方式。能夠用Swing Constants接口中的常量來指定排列方式。在這個接口中定義了幾個頗有用的常量,如LEFT、RIGHT、CENTER、NORTH、EAST等。JLabel是實現這個接口的一個Swing類。所以,能夠指定右對齊標籤:
JLabel label = new JLabel("User name:",SwingConstants.RIGHT);
或者
JLabel label = new JLabel("User name:",JLabel.RIGHT);
利用setText和setlcon方法能夠在運行期間設置標籤的文本和圖標。
提示:能夠在按鈕、標籤和菜單項上使用無格式文本或HTML文本。咱們不推薦在按鈕上使用HTML文本---這樣會影響觀感。可是HTML文本在標籤中是很是有效的。只要簡單地將標籤字符串放置在
<html>...</html>
中便可:label = new JLabel("<html><b>Required</b> entry:</html>");須要說明的是包含HTML標籤的第一個組件須要延遲一段時間才能顯示出來,這是由於須要加載至關複雜的HTML顯示代碼。
與其餘組件同樣,標籤也能夠放置在容器中。這就是說,能夠利用前面介紹的技巧將標籤放置在任何須要的地方。
javax.swing.JLabel1.2
JLabel(String text); JLabel(Icon icon); JLabel(String text, int align); JLabel(String text, Icon Icon, int align); //構造一個標籤。 //參數:text標籤中的文本 // icon標籤中的圖標 // align一個SwingConstants的常量LEFT (默認)、CENTER或者RIGHT String getText(); void setText(String text); //獲取或設置標籤的文本。 Icon getIcon(); void setIcon(Icon Icon); //獲取或設置標籤的圖標。
3.3 密碼域
密碼域是一種特殊類型的文本域。爲了不有不良企圖的人看到密碼, 用戶輸入的字符不顯示出來。每一個輸入的字符都用回顯字符(echo character) 表示, 典型的回顯字符是星號 (*)。Swing 提供了 JPasswordField類來實現這樣的文本域。
密碼域是另外一個應用模型-視圖控制器體系模式的例子。密碼域採用與常規的文本域相同的模型來存儲數據, 可是,它的視圖卻改成顯示回顯字符,而不是實際的字符。
javax.swing.JPasswordField 1.2
JPasswordField(String text, int columns); //構造一個新的密碼域對象。 void setEchoChar(char echo); //爲密碼域設置回顯字符。注意:獨特的觀感能夠選擇本身的回顯字符。0表示從新設置爲默認的回顯字符。 char[] getPassworci(); //返回密碼域中的文本。爲了安全起見,在使用以後應該覆寫返回的數組內容(密碼並非以String的形式返回,這是由於字符串在被垃圾回收器回收以前會一直駐留在虛擬機中)。
3.4 文本區
有時,用戶的輸入超過一行。正像前面提到的,須要使用JTextArea組件來接收這樣的輸入。當在程序中放置一個文本區組件時,用戶就能夠輸入多行文本,並用ENTER鍵換行。每行都以一個「 \n」 結尾。圖12-13顯示了一個工做的文本區。
在JTextArea組件的構造器中,能夠指定文本區的行數和列數。例如:
textArea = new JTextArea(8, 40); // 8 lines of 40 columns each
與文本域同樣。出於穩妥的考慮,參數columns應該設置得大一些。另外,用戶並不受限於輸入指定的行數和列數。當輸入過長時,文本會滾動。還能夠用setColumns方法改變列數,用setRows方法改變行數。這些數值只是首選大小---佈局管理器可能會對文本區進行縮放。
若是文本區的文本超出顯示的範圍,那麼剩下的文本就會被剪裁掉。能夠經過開啓換行特性來避免裁剪過長的行:
textArea.setLineWrap(true); //long lines are wrapped
換行只是視覺效果;文檔中的文本沒有改變,在文本中並無插入「 \n」字符。
3.5 滾動窗格
在Swing中,文本區沒有滾動條。若是須要滾動條,能夠將文本區插入到滾動窗格 (scroll pane) 中。
textArea = new JTextArea(8, 40); JScrollPane scrollPane = new JScratlPane(textArea);
如今滾動窗格管理文本區的視圖。若是文本超出了文本區能夠顯示的範圍,滾動條就會自動地出現,而且在刪除部分文本後,當文本可以顯示在文本區範圍內時,滾動條會再次自動地消失。滾動是由滾動窗格內部處理的,編寫程序時無需處理滾動事件。
這是一種爲任意組件添加滾動功能的通用機制,而不是文本區特有的。也就是說,要想爲組件添加滾動條,只需將它們放入一個滾動窗格中便可。
程序清單12-2展現了各類文本組件。這個程序只是簡單地顯示了一個文本域、一個密碼域和一個帶滾動條的文本區。文本域和密碼域都使用了標籤。點擊「Insert」會將組件中的內容插入到文本區中。
註釋:JTextArea組件只顯示無格式的文本,沒有特殊字體或者格式設置。若是想要顯示格式化文本(如HTML), 就須要使用JEditorPane類。在卷II將詳細討論。
程序清單 12-2 text/TextComponentFrame.java
package text; import java.awt.BorderLayout; import java.awt.GridLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JScrollPane; import javax.swingJTextArea; import javax.swingJTextField; import javax.swing.SwingConstants; /** * A frame with sample text components. */ public class TextComponentFrame extends JFrame { public static final int TEXTAREA.ROWS = 8; public static final int TEXTAREA.COLUMNS = 20; public TextComponentFrame() { JTextField textField = new JTextField(); JPasswordField passwordField = new JPasswordseld(); JPanel northPanel = new JPanel(); northPanel.setLayout(new CridLayout(2, 2)); northPanel.add(new JLabel("User name:",SwingConstants.RIGHT)); northPanel.add(textField); northPanel.add(new JLabel("Password:"SwingConstants.RIGHT)); northPanel.add(passwordField); add(northPanel, BorderLayout.NORTH); JTextArea textArea = new JTextArea(TEXTAREA_ROWS, TEXTAREA_COLUMNS); JScrollPane scrollPane = new JScrollPane(textArea); add(scrollPane, BorderLayout.CENTER); // add button to append text into the text' area JPanel southPanel = new JPanel(); JButton insertButton = new JButton("Insert"); southPanel.add(insertButton); insertButton.addActionListener(event->textArea.append("User name:"+textField.getText()+ "Password:+new String(passwordField.getPassword())+"\n")); add(southPanel,BorderLayout.SOUTH); pack(); } }
javax.swing.JTextArea 1.2
JTextArea(); JTextArea(int rows, int cols); JTextArea(String text, int rows, int cols); //構造一個新的文本區對象。 void setColumns(int cols); //設置文本區應該使用的首選列數。 void setRows(int rows); //設置文本區應該使用的首選行數。 void append(String newText); //將給定的文本追加到文本區中已有文本的尾部。 void setLineWrap(boolean wrap); //打開或關閉換行。 void setWrapStyleWord(boolean word); //若是word是true, 超長的行會在字邊框處換行。若是爲false,超長的行被截斷而不考慮字邊框。 void SETTABSIZG(int c); //將製表符(tab stop) 設置爲c列。注意,製表符不會被轉化爲空格,但可讓文本對齊到下一個製表符處。javax.swing.JScroliPane1.2
JScrol1Pane(Component c); //建立一個滾動窗格,用來顯示指定組件的內容。當組件內容超過顯示範圍時,滾動條會自動地出現。
4 選擇組件
前面已經講述瞭如何獲取用戶輸入的文本。然而,在不少狀況下,可能更加願意給用戶幾種選項,而不讓用戶在文本組件中輸入數據。使用一組按鈕或者選項列表讓用戶作出選擇 (這樣也免去了檢查錯誤的麻煩)。在本節中,將介紹如何編寫程序來實現複選框、單選按鈕、選項列表以及滑塊。
4.1 複選框
若是想要接收的輸入只是「是」 或「非」,就可使用複選框組件。複選框自動地帶有標識標籤。用戶經過點擊某個複選框來選擇相應的選項, 再點擊則取消選取。當複選框得到焦點時,用戶也能夠經過按空格鍵來切換選擇。
圖12-14所示的程序中有兩個複選框,其中一個用於打開或關閉字體傾斜屬性,而另外一個用於控制加粗屬性。注意,第二個複選框有焦點,這一點能夠由它周圍的矩形框看出。只要用戶點擊某個複選框,程序就會刷新屏幕以便應用新的字體屬性。
複選框須要一個緊鄰它的標籤來講明其用途。在構造器中指定標籤文本。
bold = new JCheckBox("Bold");
可使用setSelected方法來選定或取消選定複選框。例如:
bold.setSelected(true);
isSelected方法將返回每一個複選框的當前狀態。若是沒有選取則爲false, 不然爲true。當用戶點擊複選框時將觸發一個動做事件。一般,能夠爲複選框設置一個動做監聽器。在下面程序中,兩個複選框使用了同一個動做監聽器。
ActionListener listener =... bold.addActionListener(listener); italic.addActionListener(listener);
actionPerformed方法查詢bold和italic兩個複選框的狀態,而且把面板中的字體設置爲常規、加粗、傾斜或者粗斜體。
ActionListener listener = event -> { int mode = 0; if (bold.isSelectedO) mode += Font.BOLD; if (italic.isSelectedO) mode += Font.ITALIC; label.setFont(new Font(Font.SERIF, mode, FONTSIZE)); };
程序清單 12-3 給出了複選框例子的所有代碼。
程序清單 12-3 checkBox/CheckBoxTest.java
package checkBox; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * A frame with a sample text label and check boxes for selecting font * attributes. */ public class CheckBoxFrame extends JFrame { private JLabel label; private JCheckBox bold; private JCheckBox italic; private static final int FONTSIZE = 24; public CheckBoxFrame() { // add the sample text label label = new JLabel("The quick brown fox jumps over the lazy dog."); label.setFont(new Font("Serif", Font.BOLD, FONTSIZE)); add(label, BorderLayout.CENTER); // this listener sets the font attribute of // the label to the check box state ActionListener listener = event -> { int mode = 0; if (bold.isSelectedO) mode += Font.BOLD; if (italic.isSelectedO) mode += Font.ITALIC; label.setFont(new Font("Serif", mode, FONTSIZE)); }; // add the check boxes JPanel buttonPanel = new JPanel(); bold = new JCheckBox("Bo1d"); bold.addActionListener(listener); bold.setSelected(true); buttonPanel.add(bold); italic = new JCheckBox("Italic"); italic.addActionListener(listener); buttonPanel.add(italic); add(buttonPanel, BordedLayout.SOUTH); pack(); } }
javax.swing.JCheckBox 1.2
JCheckBox(String label); JCheckBox(String label,Icon icon); //構造一個複選框, 初始沒有被選擇。 JCheckBox(String label,boolean state); //用給定的標籤和初始化狀態構造一個複選框。 boolean isSelected (); void setSelected(boolean state); //獲取或設置複選框的選擇狀態。
4.2 單選鈕
在前一個例子中,對於兩個複選框,用戶既能夠選擇一個、兩個,也能夠兩個都不選。在不少狀況下,咱們須要用戶只選擇幾個選項當中的一個。當用戶選擇另外一項的時候, 前一項就自動地取消選擇。這樣一組選框一般稱爲單選鈕組(RadioButtonGroup), 這是由於這些 按鈕的工做很像收音機上的電臺選擇按鈕。當按下一個按鈕時,前一個按下的按鈕就會自動 彈起。圖12-15給出了一個典型的例子。這裏容許用戶在多個選擇中選擇字體的大小,即小、中、大和超大,可是,每次用戶只能選擇一個。
在Swing中,實現單選鈕組很是簡單。爲單選鈕組構造一個ButtonGroup的對象。而後,再將JRadioButton類型的對象添加到按鈕組中。按鈕組負責在新按鈕被按下時,取消前一個被按下的按鈕的選擇狀態。
ButtonGroup group = new ButtonGroup(); JRadioButton smallButton = new JRadioButton("Small", false); group.add(smallButton); JRadioButton mediumButton = new JRadioButton("Medium", true); group.add(mediumButton); ...
構造器的第二個參數爲true代表這個按鈕初始狀態是被選擇,其餘按鈕構造器的這個參數爲false。注意,按鈕組僅僅控制按鈕的行爲,若是想把這些按鈕組織在一塊兒佈局,須要把它們添加到容器中,如JPanel。
單選鈕與複選框的外觀是不同的。複選框爲正方形,而且若是被選擇,這個正方形中會出現一個對鉤的符號。單選鈕是圓形,選擇之後圈內出現一個圓點。 單選鈕的事件通知機制與其餘按鈕同樣。當用戶點擊一個單選鈕時, 這個按鈕將產生一個動做事件。在示例中,定義了一個動做監聽器用來把字體大小設置爲特定值:
ActionListener listener = event -> label.setFont(new Font("Serif", Font.PLAIN, size));
用這個監聽器與複選框中的監聽器作一個對比。每一個單選鈕都對應一個不一樣的監聽器對象。每一個監聽器都很是清楚所要作的事情---把字體尺寸設置爲一個特定值。在複選框示例中,使用的是一種不一樣的方法,兩個複選框共享一個動做監聽器。這個監聽器調用一個方法來檢查兩個複選框的當前狀態。
對於單選鈕可使用同一個方法嗎?能夠試一下使用一個監聽器來計算尺寸,如:
if (smallButton.isSelected()) size = 8; else if (mediumButton.isSelected()) size = 12; ...
然而,更願意使用各自獨立的動做監聽器,由於這樣能夠將尺寸值與按鈕緊密地綁定在一塊兒。
註釋:若是有一組單選鈕,並知道它們之中只選擇了一個。要是可以不查詢組內全部的按鈕就能夠很快地知道哪一個按鈕被選擇的話就行了。因爲ButtonGroup對象控制着全部的按鈕,因此若是這個對象可以給出被選擇的按鈕的引用就方便多了。事實上,ButtonGroup類中有一個getSelection方法,可是這個方法並不返回被選擇的單選鈕,而是返回附加在那個按鈕上的模型ButtonModel的引用。對於咱們來講,ButtonModel中的方法沒有什麼實際的應用價值。ButtonModel接口從ItemSelectable接口繼承了一個getSelectedObject方法,可是這個方法沒有用,它返回null。getActionCommand方法看起來彷佛可用,這是由於一個單選鈕的「動做命令」是它的文本標籤,可是它的模型的動做命令是null。只有在經過setActionCommand命令明確地爲全部單選鈕設定動做命令後,纔可以經過調用方法
buttonGroup.getSelection().getActionCommand()得到當前選擇的按鈕的動做命令。
程序清單 12-4是一個用於選擇字體大小的完整程序,它演示了單選鈕的工做過程。
package radioButton; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * A frame with a sample text label and radio buttons for selecting font sizes. */ public class RadioButtonPrame extends JFrame { private JPanel buttonPanel: private ButtonGroup group; private JLabel label; private static final int DEFAULT.SIZE = 36; public RadioButtonFrame() { // add the sample text label label = newJLabel("The quick brown fox jumps over the lazy dog."); label.setFont(new Font("Serif", Font.PLAIN, DEFAULT_SIZE)); add(label, BorderLayout.CENTER); // add the radio buttons buttonPanel = new JPanel(); group = new BtittonGroup(); addRadioButton("Small", 8); addRadioButton("Medium", 12); addRadioButton("Large", 18); addRadioButton("Extra large", 36); add(buttonPanel,BorderLayout.SOUTH); pack(); } /** * Adds a radio button that sets the font size of the sample text. * @param name the string to appear on the button *@param size the font size that this button sets **/ public void addRadioButton(String name, int size) { boolean selected = size == DEFAULT_SIZE; JRadioButton button = new JRadioButton(name, selected); group.add(button); buttonPanel.add(button); // this listener sets the label font size ActionListener listener = event -> label.setFont(new Font("Serif", Font.PLAIN, size)); button.addActionListener(listener); } }
javax.swing.JRadioButton 1.2
JRadioButton(String label, Icon icon); //構造一個單選鈕, 初始沒有被選擇。 JRadioButton(String label, boolean state); //用給定的標籤和初始狀態構造一個單選鈕。
javax.swing.ButtonGroup 1.2
void add(AbstractButton b); //將按鈕添加到組中。 ButtonModel getSelection(); //返回被選擇的按鈕的按鈕模型。
javax.swing.ButtonModel 1.2
String getActionCommand(); //返回按鈕模型的動做命令。
javax.swing.AbstractButton 1.2
void setActionCommand(String s); //設置按鈕及其模型的動做命令
4.3 邊框
若是在一個窗口中有多組單選按鈕,就須要用可視化的形式指明哪些按鈕屬於同一組。Swing提供了一組頗有用的邊框(borders) 來解決這個問題。能夠在任何繼承了JComponent的組件上應用邊框。最經常使用的用途是在一個面板周圍放置一個邊框,而後用其餘用戶界面元素(如單選鈕)填充面板。
有幾種不一樣的邊框可供選擇,可是使用它們的步驟徹底同樣。
1 ) 調用 BorderFactory 的靜態方法建立邊框。下面是幾種可選的風格
- 凹斜面
- 凸斜面
- 蝕刻
- 直線
- 蒙版
- 空(只是在組件外圍建立一些空白空間)
2 ) 若是願意的話,能夠給邊框添加標題,具體的實現方法是將邊框傳遞給BroderFactory.createTitledBorder
3 ) 若是確實想把一切凸顯出來,能夠調用下列方法將幾種邊框組合起來使用 BorderFactory.createCompoundBorder
4 ) 調用JComponent類中setBorder方法將結果邊框添加到組件中。
例如,下面代碼說明了如何把一個帶有標題的蝕刻邊框添加到一個面板上:
Border etched = BorderFactory.createEtchedBorder(); Border titled = BorderFactory.createTitledBorder(etched, "A Title"); panel.setBorder(titled);
運行程序清單12-5中的程序能夠看到各類邊框的外觀。
不一樣的邊框有不一樣的用於設置邊框的寬度和顏色的選項。詳情請參看API註釋。偏心使用邊框的人都很欣賞這一點,SoftBevelBorder類用於構造具備柔和拐角的斜面邊框,LineBorder類也可以構造圓拐角。這些邊框只能經過類中的某個構造器構造,而沒有BorderFactory方法。
javax.swing.BorderFactory 1.2
static Border createLineBorder(Co1or color); static Border createLineBorder(Color color, int thickness); //建立一個簡單的直線邊框。 static MatteBorder createMatteBorder(int top, int left, int bottom, int right, Color color); static MatteBorder createMatteBorder(int top, int left, int bottom, int right, Icon tilelcon); //建立一個用 color 顏色或一個重複(repeating) 圖標填充的粗的邊框。 static Border createEmptyBorder(); static Border createEmptyBorder(int top, int left, int bottom, int right); //建立一個空邊框。 static Border createEtchedBorder(); static Border createEtchedBorder(Color highlight,Color shadow); static Border createEtchedBorder(int type); static Border createEtchedBorder(int type,Color highlight,Color shadow); //建立一個具備 3D 效果的直線邊框。 //參數: highlight, shadow 用於3D效果的顏色 //type EtchedBorder.RAISED 和 EtchedBorder.LOWERED 之一 static Border createBevelBorder (int type); static Border createBevelBorder(int type, Color highlight, Color shadow); static Border createLoweredBevelBorder(); static Border createRaisedBevelBorder(); //建立一個具備凹面或凸面效果的邊框。 //參數:type BevelBorder.LOWERED 和 BevelBorder.RAISED 之一 //highlight,shadow 用於 3D 效果的顏色 static TitledBorder createTitledBorder(String title); static TitledBorder createTitledBorder(Border border); static TitledBorder createTitledBorder(Border border, String title); static TitledBorder createTitledBorder(Border border, String title, int justification, Int position); static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font); static Tit edBorder createTitledBorder(Border border, String title, int justification, int position, Font font, Color color); //建立一個具備給定特性的帶標題的邊框。 //參數: title 標題字符串 //border 用標題裝飾的邊框 //justification TitledBorder常量LEFT、CENTER、 RIGHT、 LEADING、 TRAILING 或 DEFAULT_JUSTIFICATION (left) 之一 //position TitledBorder常量ABOVE—TOP、TOP、BELOW—TOP、ABOVE_ BOTTOM、 BOTTOM、 BELOW_BOTTOM 或 DEFAULT— POSITION (top) 之一 //font 標題的字體 //color 標題的顏色 static CompoundBorder createCompoundBorder(Border outsideBorder, Border insideBorder); //將兩個邊框組合成一個新的邊框。 position font color
javax.swing,border.SoftBevelBorder 1.2
SoftBevelBorder(int type); SoftBevelBorder(int type, Color highlight, Color shadow); //建立一個帶有柔和邊角的斜面邊框。 //參數:type BevelBorder.LOWERED和BevelBorder.RAISED之一 //highlight,shadow 用於3D效果的顏色
javax.swing.border.LineBorder 1.2
public LineBorder(Color color, int thickness, boolean roundedCorners); //用指定的顏色和粗細建立一個直線邊框。若是roundedCorners爲true,則邊框有圓角。
javax.swing.JComponent 1.2
void setBorder(Border border); //設置這個組件的邊框
4.4 組合框
若是有多個選擇項, 使用單選按鈕就不太適宜了,其緣由是佔據的屏幕空間太大。這時就能夠選擇組合框。當用戶點擊這個組件時,選擇列表就會下拉出來,用戶能夠從中選擇一項(見圖 12-17 )。
若是下拉列表框被設置成可編輯( editable), 就能夠像編輯文本同樣編輯當前的選項內容。鑑於這個緣由,這種組件被稱爲組合框(combo box), 它將文本域的靈活性與一組預約義的選項組合起來。JComboBox類提供了組合框的組件。
在Java SE 7中,JComboBox 類是一個泛型類。例如,JComboBox<String>
包含String類型的對象,JComboBox<Integer>
包含整數。
調用setEditable方法可讓組合框可編輯。注意,編輯只會影響當前項,而不會改變列表內容。
能夠調用getSelectedltem方法獲取當前的選項,若是組合框是可編輯的,當前選項則是能夠編輯的。不過,對於可編輯組合框, 其中的選項能夠是任何類型,這取決於編輯器(即 由編輯器獲取用戶輸人並將結果轉換爲一個對象)。關於編輯器的討論請參見卷n中的第6 章。若是你的組合框不是可編輯的, 最好調用
combo.getltemAt(combo.getSelectedlndex())
這會爲所選選項提供正確的類型。
在示例程序中,用戶能夠從字體列表(Serif, SansSerif,Monospaced 等)中選擇一種字體,用戶也能夠鍵入其餘的字體。
能夠調用addltem方法增長選項。在示例程序中,只在構造器中調用了addltem方法,實際上,能夠在任何地方調用它。
JComboBox<String> faceCombo = new JComboBox<>(); faceCombo.addItem("Serif"); faceCombo.addItem("SansSerif");
這個方法將字符串添加到列表的尾部。能夠利用 insertltemAt方法在列表的任何位置插入一個新選項:
faceCombo.insertItemAt("Monospaced", 0);// add at the beginning
能夠增長任何類型的選項,組合框能夠調用每一個選項的toString方法顯示其內容。
若是須要在運行時刪除某些選項,可使用removeltem或者removeltemAt方法,使用哪一個方法將取決於參數提供的是想要刪除的選項內容,仍是選項位置。
faceCombo.removeItem("Monospaced"); faceCombo.removeItemAt(0); // remove first item
調用removeAllltems方法將當即移除全部的選項。
提示:若是須要往組合框中添加大量的選項,addltem方法的性能就顯得不好了。取而代之的是構造一個DefaultComboBoxModel, 並調用addElement方法進行加載, 而後再調用JComboBox中的setModel方法。
當用戶從組合框中選擇一個選項時,組合框就將產生一個動做事件。爲了判斷哪一個選項被選擇,能夠經過事件參數調用getSource方法來獲得發送事件的組合框引用,接着調用getSelectedltem方法獲取當前選擇的選項。須要把這個方法的返回值轉化爲相應的類型,一般是String型。
ActionListener listener = event -> label.setFont(new Font(faceCombo.getItemAt(faceCombo.setSelectedIndex()),Font.PLAIN,DEFAULT_SIZE));
程序清單12-6給出了完整的代碼。
註釋:若是但願持久地顯示列表,而不是下拉列表,就應該使用JList組件。在卷Ⅱ的第6章中將介紹JList
程序清單 12-6 comboBox/ComboBoxFrame.java
package comboBox; import java.awt.BorderLayout; import java.awt.Font; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; /** * A frame with a sample text label and a combo box for selecting font faces. */ public class ComboBoxFrame extends JFrame { private JComboBox<String> faceCombo; private JLabel label; private static final int DEFAULT.SIZE = 24; public ComboBoxFrame() { // add the sample text label label = new JLabel("The quick brown fox jumps over the lazy dog."); label.setPont(new FontCSerif, Font.PLAIN, DEFAULT.SEE)); add(label,BorderLayout.CENTER); // make a combo box and add face names faceCombo = new JComboBox<>(); faceCombo.addltern("Serif"); faceCombo.addItem("SansSerif"); faceCombo.addItem("Monospaced"); faceCombo.addltem("Dialog"); faceCombo.addltem("Dialoglnput"); // the combo box listener changes the label font to the selected face name faceCombo.addActionListener(event -> label.setFont( new Font(faceCombo.getltemAt(faceCombo.getSelectedlndex()), Font.PLAIN, DEFAULT.SIZE))); // add combo box to a panel at the frame's southern border JPanel comboPanel = new JPanel(); comboPanel.add(faceCombo); add(comboPanel, BorderLayout.SOUTH); pack(); } }
javax.swing.JComboBox 1.2
boolean isEditable(); void setEditable(boolean b); //獲取或設置組合框的可編輯特性。 void addItem(Object item); //把一個選項添加到選項列表中。 void insertltemAtCObject item, int index); //將一個選項添加到選項列表的指定位置。 void removeItem(Object item); //從選項列表中刪除一個選項。 void removeItemAt(int index); //刪除指定位置的選項。 void removeAllItems(); //從選項列表中刪除全部選項。 Object getSelectedItem(); //返回當前選擇的選項。
4.5 滑動條
組合框可讓用戶從一組離散值中進行選擇。滑動條容許進行連續值的選擇,例如,從1~100之間選擇任意數值。
一般,可使用下列方式構造滑動條:
JSlider slider = new JSlider(min, max, initialValue);
若是省略最小值、最大值和初始值,其默認值分別爲0、100和50。
或者若是須要垂直滑動條,能夠按照下列方式調用構造器:
JSlider slider = new JSlider(SwingConstants.VERTICAL, min, max, initialValue);
這些構造器構造了一個無格式的滑動條, 如圖12-18最上面的滑動條所示。下面看一下如何爲滑動條添加裝飾。
當用戶滑動滑動條時, 滑動條的值就會在最小值和最大值之間變化。當值發生變化時,ChangeEvent就會發送給全部變化的監聽器。爲了獲得這些改變的通知,須要調用addChangeListener方法而且安裝一個實現了ChangeListener接口的對象。這個接口只有一個方法StateChanged。在這個方法中,能夠獲取滑動條的當前值:
ChangeListener listener = event -> { JSlider slider = (JSlider) event.getSource(); int value = slider.getValue(); ... };
能夠經過顯示標尺(tick)對滑動條進行修飾。例如,在示例程序中,第二個滑動條使用了下面的設置:
slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(5);
上述滑動條在每20個單位的位置顯示一個大標尺標記,每5個單位的位置顯示一個小標尺標記。所謂單位是指滑動條值,而不是像素。
這些代碼只設置了標尺標記,要想將它們顯示出來,還須要調用:
slider.setPaintTicks(true);
大標尺和小標尺標記是相互獨立的。例如,能夠每 20 個單位設置一個大標尺標記,同時每 7 個單位設置一個小標尺標記, 可是這樣設置,滑動條看起來會顯得很是凌亂。
能夠強制滑動條對齊標尺。這樣一來,只要用戶完成拖放滑動條的操做, 滑動條就會當即自動地移到最接近的標尺處。激活這種操做方式須要調用:
slider.set5napToTicks(true);
註釋: 「對齊標尺」的行爲與想象的工做過程並不太同樣。在滑動條真正對齊以前,改變監聽器報告的滑動條值並非對應的標尺值。若是點擊了滑動條附近,滑動條將會向點 擊的方向移動一小段距離,「對奪標尺」的滑塊並不移動到下一個標尺處。
能夠調用下列方法爲大標尺添加標尺標記標籤(tick mark labels):
slider.setPaintLabels(true);
例如, 對於一個範圍爲 0 到 100 的滑動條, 若是大標尺的間距是 20, 每一個大標尺的標籤 就應該分別是 0、20、40、60、80 和 100。
還能夠提供其餘形式的標尺標記,如字符串或者圖標(見圖 12-18 )。這樣作有些煩瑣。 首先須要填充一個鍵爲Integer類型且值爲Component類型的散列表。 而後再調用setLabelTable方法,組件就會放置在標尺標記處。一般組件使用的是幾此以對象。下面代碼 說明了如何將標尺標籤設置爲 A、B、C、D、E 和 F。
Hashtable<Integer, Component> labelTable = new Hashtable<Integer, Components>(); labelTable.put(0, new JLabel("A")); labelTable.put(20, new Jabel("B")); labelTable.put(100, new Jabel("F")); slider.setLabelTable(labelTable);
關於散列表的詳細介紹, 參考第 9 章。
程序清單12-7顯示瞭如何建立用圖標做爲標尺標籤的滑動條。
提示: 若是標尺的標記或者標籤不顯示,請檢查一下是否調用了setPaintTicks(true)和setPaintLabels(true)。
在圖 12-18 中, 第 4 個滑動條沒有軌跡。要想隱藏滑動條移動的軌跡,能夠調用:
slider.setPaintTrack(false);
圖 12-18 中第 5 個滑動條是逆向的,調用下列方法能夠實現這個效果:
slider.setlnverted(true);
示例程序演示了全部不一樣視覺效果的滑動條。每一個滑動條都安裝了一個改變事件監聽器,它負責把當前的滑動條值顯示到框架底部的文本域中
程序清單 12-7 slider/SliderFrame.java
package slider; import java.awt.*; import java.util.*; import javax.swing.*; import javax.swing.event.*; /** * A frame with many sliders and a text field to show slider values. */ public class SliderFrame extends JFrame { private JPanel sliderPanel; private JTextField textField; private ChangeListener listener; public Slide JFrame() { sliderPanel = new JPanel(); sliderPanel.setLayout(new GridBagLayout()); // common listener for all sliders listener = event -> { // update text field when the slider value changes JSlider source = (JSIider) event.getSource(); textField.setText("" + source.getValue()); }; // add a plain slider JSlider slider = new JSlider(); addSlider(slider, "Plain"); // add a slider with major and minor ticks slider = new JSlider(); slider.setPaintTicks(true); slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(5); addSlider(slider, "Ticks"); // add a slider that snaps to ticks slider = new JSlider(); slider.setPaintTicks(true); slider.setSnapToTicks(true); slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(5); addSlider(slider, "Snap to ticks"); // add a slider with no track slider = new JSlide(); slider.setPaintTicks(true); slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(5); slider.setPaintTrack(false); addSlider(slider, "No track"); // add an inverted slider slider = new JSlider(); slider.setPaintTicks(true); slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(5); slider.setlnverted(true); addSlider(slider, "Inverted"); // add a slider with numeric labels slider = new JSlider(); slider.setPaintTicks(true); slider.setPaintLabels(true); siider.setMajorTickSpacing(20); slider.setMinorTickSpacing(5); addSlider(slider, "Labels"); // add a slider with alphabetic labels slider = new JSlider(); siider.setPaintLabels(true); slider.setPaintTicks(true); slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(5); Dictionary<lnteger, Component>labelTable = new Hashtable<>(); labelTable.put(0,new JLabel("A")); labelTable.put(20, new JLabel("B")); labelTable,put(40, new JLabel("C")); labelTable.put(60, new JLabel("D")); labelTable.put(80, new JLabel("E")); labelTable.put(100, new JLabel("F")); slider.setLabelTable(labelTable); addSlider(slider, "Custom labels"); // add a slider with icon labels slider = new JSlider(); slider.setPaintTicks(true); slider.setPaintLabels(true); siider.setSnapToTicks(true); slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(20); labelTable = new Hashtable<Integer, Component>(); // add card images labelTable.put(0, new JLabel(new Imagelcon("nine.gif"))); labelTable.put(20, new JLabel(new ImagelconC"ten.gif"))); labelTable.put(40, new JLabel(new Imagelcon("jack,gif"))); labelTable.put(60, new JLabel(new Imagelcon("queen,gif"))); labelTable.put(80, new JLabel(new Imagelcon("king.gif"))); labelTable.put(100, new JLabel(new Imagelcon("ace.gif"))); slider.setLabelTable(labelTable); addSlider(slider, "Icon labels"); // add the text field that displays the slider value textField = new JTextField(); add(s1iderPane1, BorderLayout.CENTER); add(textField, BorderLayout.SOUTH); pack(); } /** * Adds a slider to the slider panel and hooks up the listener * @param s the slider * @param description the slider description */ public void addSlider(Slider s, String description) { s.addChangeListener(listener); jPanel panel = new JPanel(); panel.add(s); panel.add(new JLabel(description)); panel.setAlignmentX(Component.LEFT_ALIGNMENT); GridBagConstraints gbc = new CridBagConstraints(); gbc.gridy = sliderPanel.getComponentCount(); gbc.anchor = gridBagConstraints.WEST; slidePanel.add(panel, gbc); } }
javax.swing.JSlider 1.2
JSlider(); JSlider(int direction); JSlider(int min, int max); JSlider( int min, int max, int initialValue); JSlider(int direction, int min, int max, int initialValue); //用給定的方向、最大值、 最小值和初始化值構造一個水平滑動條。 //參數: direction SwingConstants.HORIZONTAL或 SwingConstants.VERTICAL之一。默認爲水平。 //min, max 滑動條的最大值、最小值。默認值爲0到100。 //initialValue 滑動條的初始化值。默認值爲50。 void setPaintTicks(boolean b); //若是b爲true, 顯示標尺。 void setMajorTickSpacing(int units); void setMinorTickSpacing(int units); //用給定的滑動條單位的倍數設置大標尺和小標尺。 void setPaintLabels(boolean b); //若是b是ture,顯示標尺標籤。 void setLabelTab e(Dictionary table); //設置用於做爲標尺標籤的組件。表中的每個鍵/值對都採用new Integer(value)/component的格式 void setSnapToTicks(boolean b); //若是b是true,每一次調整後滑塊都要對齊到最接近的標尺處。 void setPaintTrack(boolean b); //若是b是ture,顯示滑動條滑動的軌跡。
5 菜 單
前面介紹了幾種最經常使用的能夠放到窗口內的組件, 如:各類按鈕、文本域以及組合框等。 Swing還提供了一些其餘種類的用戶界面兀素,下拉式菜單就是 GUI 應用程序中很常見的一種。
位於窗口頂部的菜單欄 (menubar) 包括了下拉菜單的名字。點擊一個名字就能夠打開包含菜單項 ( menu items) 和子菜單(submenus) 的菜單。 當用戶點擊菜單項時,全部的菜單都會被關閉而且將一條消息發送給程序。圖12-19顯示了一個帶子菜單的典型菜單。
5.1 菜單建立
建立菜單是一#很是容易的事情。首先要建立一個菜單欄:
JMenuBar menuBar = newJMenuBar();
菜單欄是一個能夠添加到任何位置的組件。一般放置 圖 12-19 帶有子菜單的菜單在框架的頂部。能夠調用setJMenuBar方法將菜單欄添加到框架上:
frame.setJMenuBar(menuBar);
須要爲每一個菜單創建一個菜單對象:
JMenu editMenu = new JMenu("Edit");
而後將頂層菜單添加到菜單欄中:
menuBar.add(editMenu);
向菜單對象中添加菜單項、分隔符和子菜單:
JMenuItem pasteltem = new JMenuItem("Paste"); editMenu.add(pasteltem); editMenu.addSeparator(); JMenu optionsMenu = ...; // a submenu editMenu.add(optionsMenu);
能夠看到圖12-19中分隔符位於Paste和Read-only菜單項之間。當用戶選擇菜單時,將觸發一個動做事件。這裏須要爲每一個菜單項安裝一個動做監聽器。
ActionListener listener = ...; pastelteiaddActionListener(listener);
可使用JMenu.add(Striiigs)方法將菜單項插入到菜單的尾部,例如:
editMenu.add("Paste");
Add方法返回建立的子菜單項。能夠採用下列方式獲取它,並添加監聽器:
JMenuItem pasteltem = editMenu.add("Paste"); pasteltem.addActionListener(listener);
在一般狀況下,菜單項觸發的命令也能夠經過其餘用戶界面元素(如工具欄上的按鈕) 激活。在第11章中,已經看到了如何經過 Action對象來指定命令。一般,採用擴展抽象類 AbstractAction來定義一個實現 Action 接口的類。這裏須要在AbstractAction對象的構造器中 指定菜單項標籤而且覆蓋 actionPerformed方法來得到菜單動做處理器。例如:
Action exitAction = new AbstractAction("Exit") // menu item text goes here { public void actionPerformed(ActionEvent event) { // action code goes here System.exit(0); } };
而後將動做添加到菜單中:
JMenuItem exitltem = fileMenu.add(exitAction);
這個命令利用動做名將一個菜單項添加到菜單中。這個動做對象將做爲它的監聽器。上面這條語句是下面兩條語句的快捷形式:
JMenuItem exitltem = new JMenuItem(exitAction); fileMenu.add(exitltem);
javax.swing.JMenu1.2
JMenu(String label); //用給定標籤構造一個菜單。 JMenuItem add(JMenuItem item); //添加一個菜單項(或一個菜單)。 JMenuItem add(String label); //用給定標籤將一個菜單項添加到菜單中, 並返回這個菜單項。 JMenuItem add(Action a); //用給定動做將一個菜單項添加到菜單中, 並返回這個菜單項。 void addSeparator(); //將一個分隔符行(separatorline)添加到菜單中。 JMenuItem insert(JMenuItem menu, int index); //將一個新菜單項(或子菜單)添加到菜單的指定位置。 JMenuItem insert(Action a, int index); //用給定動做在菜單的指定位置添加一個新菜單項。 void insertSeparator(int index); //將一個分隔符添加到菜單中。 //參數:index 添加分隔符的位置 void remove(int index); void remove(JMenuItem item); //從菜單中刪除指定的菜單項。
javax.swing.JMenuItem1.2
JMenuItem(String label); //用給定標籤構造一個菜單項。 JMenuItem(Action a)1.3 //爲給定動做構造一個菜單項。
javax.swing.AbstractButton1.2
void setAction(Action a)1.3 //爲這個按鈕或菜單項設置動做
javax.swing.JFrame1.2
void setJMenuBar(JMenuBar menubar); //爲這個框架設置菜單欄。
5.2 菜單項中的圖標
菜單項與按鈕很類似。實際上,JMenuItem類擴展了AbstractButton類。與按鈕同樣,菜單能夠包含文本標籤、圖標,也能夠二者都包含。既能夠利用 JMenuItem(String,Icon) 或者 JMenuItem(Icon) 構造器爲菜單指定一個圖標,也能夠利用JMenuItem類中的setlcon方法 (繼承自AbstractButton類)指定一個圖標。例如:
JMenuItem cutltem = new JMenuItem("Cut", new Imagelcon("cut.gif"));
圖12-19展現了具備圖標的菜單項。正如所看到的,在默認狀況下,菜單項的文本被放置在 圖標的右側。若是喜歡將文本放置在左側,能夠調用JMenuItem類中的setHorizontalTextPosition方法(繼承自 AbstractButton 類)設置。例如:
cutltem.setHorizontalTextPosition(SwingConstants.LEFT);
這個調用把菜單項文本移動到圖標的左側。 也能夠將一個圖標添加到一個動做上:
cutAction.putValue(Action.SMALL.ICON, new Imagelcon("cut.gif"));
當使用動做構造菜單項時,Action.NAME值將會做爲菜單項的文本,而Action.SMALL_ ICON將會做爲圖標。 另外,能夠利用AbstractAction構造器設置圖標:
cutAction = new AbstractAction("Cut", new Imagelcon("cut.gif")) { public void actionPerformed(ActionEvent event) { ... } };
javax.swing.JMenuItem1.2
JMenuItem(String label, Icon icon); //用給定的標籤和圖標構造一個菜單項。
javax.swing.AbstractButton1.2
void setHorizontalTextPosition(int pos); //設置文本對應圖標的水平位置。 //參數:pos SwingConstants.RIGHT(文本在圖標的右側)或SwingConstants.LEFTjavax.swing.AbstractAction1.2
AbstractAction(String name, Icon smallIcon); //用給定的名字和圖標構造一個抽象的動做。
5.3 複選框和單選鈕菜單項
複選框和單選鈕菜單項在文本旁邊顯示了一個複選框或一個單選鈕(參見圖 12-19)。當用戶選擇一個菜單項時,菜單項就會自動地在選擇和未選擇間進行切換。除了按鈕裝飾外,同其餘菜單項的處理同樣。例如,下面是建立複選框菜單項的代碼:
JCheckBoxHenuItem readonlyltem = new JCheckBoxMenuItem("Read-only"); optionsMenu.add(readonlyltem);
單選鈕菜單項與普通單選鈕的工做方式同樣,必須將它們加人到按鈕組中。當按鈕組中的一個按鈕被選中時,其餘按鈕都自動地變爲未選擇項。
ButtonGroup group = new ButtonGroup(); JRadioButtonMenuItem insertltem = new JRadioButtonMenuItem("Insert"); insertltem.setSelected(true); JRadioButtonMenuItem overtypeltem = new JRadioButtonMenuItem("Overtype"); group.add(insertltem); group.add(overtypeltem); optionsMenu.add(insertltem); optionsMenu.add(overtypeltem);
使用這些菜單項,不須要馬上獲得用戶選擇菜單項的通知。而是使用isSelected方法來測試菜單項的當前狀態(固然,這意味着應該保留一個實例域保存這個菜單項的引用)。使用 setSelected方法設置狀態。
javax.swing.JCheckBoxMenultem 1.2
JCheckBoxMenuItem(String label); //用給定的標籤構造一個複選框菜單項。 JCheckBoxMenuItem(String label, boolean state); //用給定的標籤和給定的初始狀態(true 爲選定)構造一個複選框菜單。
因javax.swing.JRadioButtonMenultem 1.2
JRadioButtonMenuItem(String label); //用給定的標籤構造一個單選鈕菜單項。 JRadioButtonMenuItem(String label , boolean state); //用給定的標籤和給定的初始狀態(true 爲選定)構造一個單選鈕菜單項。
javax.swing.AbstractButton 1.2
boolean isSelected(); void setSelected(boolean state); //獲取或設置這個菜單項的選擇狀態(true 爲選定)。
5.4 彈出菜單
彈出菜單(pop-up menu) 是不固定在菜單欄中隨處浮動的菜單(參見圖 12-20 )。
建立一個彈出菜單與建立一個常規菜單的方法相似,可是彈出菜單沒有標題。
JPopupMenu popup = new JPopupMenu();
而後用常規的方法添加菜單項:
JMenuItem item = new JMenuItem("Cut"); item.addActionListener(listener); popup.add(item);
彈出菜單並不像常規菜單欄那樣老是顯示在框架的頂部,必須調用show方法菜單才能顯示出來。調用時須要給出父組件以及相對父組件座標的顯示位置。例如:
popup.show(panel, x, y);
一般,當用戶點擊某個鼠標鍵時彈出菜單。 這就是所謂的彈出式觸發器(pop-up trigger)。在Windows或者Linux中,彈出式觸發器是鼠標右鍵。要想在用戶點擊某一個組件時彈出菜單, 須要按照下列方式調用方法:
component.setComponentPopupMenu(popup);
偶爾會遇到在一個含有彈出菜單的組件中放置一個組件的狀況。這個子組件能夠調用下 列方法繼承父組件的彈出菜單。調用:
child.setlnheritsPopupMenu(true);
javax.swing.JPopupMenu 1.2
void show(Component c, int x, int y); //顯示一個彈出菜單。 //參數: c 顯示彈出菜單的組件 //x,y 彈出菜單左上角的座標(C 的座標空間內) boolean isPopupTrigger(MouseEvent event) 1.3 //若是鼠標事件是彈出菜單觸發器,則返回 true。
java.awt.event.MouseEvent 1.1
boolean isPopupTrigger(); //若是鼠標事件是彈出菜單觸發器, 則返回 true。
javax.swing.JComponent 1.2
JPopupMenu getComponentPopupMenu() 5.0 void setComponentPopupMenu(JPopupMenu popup) 5.0 //獲取或設置用於這個組件的彈出菜單。 boolean getlnheritsPopupMenu() 5.0 void setInheritsPopupMenu(boolean b) 5.0 //獲取或設置 inheritsPopupMenu 特性。 若是這個特性被設置或這個組件的彈出菜單爲null, 則應用上一級彈出菜單。
5.5 快捷鍵和加速器
對於有經驗的用戶來講,經過快捷鍵來選擇菜單項會感受更加便捷。能夠經過在菜單項的構造器中指定一個快捷字母來爲菜單項設置快捷鍵:
JMenuItem aboutltem = new JMenuItem("About", 'A');
快捷鍵會自動地顯示在菜單項中,並帶有一條下劃線 (如圖 12-21 所示)。例如,在上面的例子中,菜單項中的標籤爲「About」,字母A帶有一個下劃線。當顯示菜單時,用戶只須要按下「 A」 鍵就能夠這個選擇菜單項(若是快捷 字母沒有出如今菜單項標籤字符串中, 一樣能夠按下快捷鍵選擇菜單項,只是快捷鍵沒有顯示出來。很天然,這種不可見的快捷鍵沒有提示效果)。
有時候不但願在菜單項的第一個快捷鍵字母下面加下劃線。例如,若是在菜單項「Save As」中使用快捷鍵「A」,則在第二個「A」 (Save As) 下面加下劃線更爲合理。能夠調用 setDisplayedMnemonicIndex方法指定但願加下劃線的字符。
若是有一個Action對象,就能夠把快捷鍵做爲Action.MNEMONIC_KEY的鍵值添加到對象中。如:
cutAction.putValue(Action,MNEMONIC_KEY, new Integer('A'));
只能在菜單項的構造器中設定快捷鍵字母,而不是在菜單構造器中。若是想爲菜單設置快捷鍵,須要調用setMnemonic方法:
JMenu helpMenu = new JMenu("Help"); helpMenu.setMnemonic('H');
能夠同時按下ALT鍵和菜單的快捷鍵來實如今菜單欄中選擇一個頂層菜單的操做。例如:按下ALT+H能夠從菜單中選擇Help菜單項。
可使用快捷鍵從當前打開的菜單中選擇一個子菜單或者菜單項。而加速器是在不打開 菜單的狀況下選擇菜單項的快捷鍵。例如: 不少程序把加速器CTRL+O和CTRL+S關聯到File菜單中的Open和Save菜單項。可使用setAccelerator將加速器鍵關聯到一個菜單項上。這個方法使用 Keystroke類型的對象做爲參數。例如:下面的調用將加速器CTRL+O關聯到Openltem菜單項。
openItem.setAccelerator(KeyStroke.getKeyStroke("ctrl 0"));
當用戶按下加速器組合鍵時,就會自動地選擇相應的菜單項,同時激活一個動做事件,這與手工地選擇這個菜單項同樣。
加速器只能關聯到菜單項上,不能關聯到菜單上。加速器鍵並不實際打開菜單。它將直接地激活菜單關聯的動做事件。
從概念上講,把加速器添加到菜單項與把加速器添加到Swing組件上所使用的技術十分相似(在第 11 章中討論了這個技術)。可是,當加速器添加到菜單項時,對應的組合鍵就會自動地顯示在相應的菜單上(見圖 12-22 )。
註釋:在Windows下,ALT+F4用於關閉窗口。 但這不是Java程序設定的加速鍵。這是操做系統定義的快捷鍵。這個組合鍵總會觸發活動窗口的WindowClosing事件,而無論菜單上是否有Close菜單項
javax.swing.JMenultem 1.2
JMenuItem(String label, int mnemonic); //用給定的標籤和快捷鍵字符構造一個菜單項。 //參數:label 菜單項的標籤 //mnemonic 菜單項的快捷鍵字符,在標籤中這個字符下面會有一個下劃線。 void setAccelerator(Keystroke k); //將k設置爲這個菜單項的加速器。加速器顯示在標籤的旁邊。
javax.swing.AbstractButton 1.2
void setMnemonic(int mnemonic); //設置按鈕的快捷字符。該字符會在標籤中如下劃線的形式顯示。 void setDisplayedMnemoniclndex(int index) 1.4 //將按鈕文本中的index字符設定爲帶下劃線。若是不但願第一個出現的快捷鍵字符帶下劃線, 就可使用這個方法。
5.6 啓用和禁用菜單項
在有些時候,某個特定的菜單項可能只可以在某種特定的環境下才可用。例如,當文檔以只讀方式打開時,Save菜單項就沒有意義。固然,可使用JMemremove方法將這個菜單項從菜單中刪掉,但用戶會對菜單內容的不斷變化感到奇怪。然而,能夠將這個菜單項設爲禁用狀態,以便屏蔽掉這些暫時不適用的命令。被禁用的菜單項被顯示爲灰色,不能被選擇它(見圖 12-23 )。
啓用或禁用菜單項須要調用setEnabled方法:
saveltem.setEnabled(false);
啓用和禁用菜單項有兩種策略。每次環境發生變化就對相關的菜單項或動做調用setEnabled。例如: 只要當文檔以只讀方式打開,就禁用Save和Save As菜單項。另外一種 方法是在顯示菜單以前禁用這些菜單項。這裏必須爲「菜單選中」 事件註冊監聽器。javax.swing.event包定義了MenuListener接口,它包含三個方法:
void menuSelected(MenuEvent event) void menuDeselected(MenuEvent event) void menuCanceled(MenuEvent event)
因爲在菜單顯示以前調用menuSelected方法,因此能夠在這個方法中禁用或啓用菜單項。下面代碼顯示了只讀複選框菜單項被選擇之後,如何禁用Save和Save As動做。
public void menuSelected(MenuEvent event) { saveAction.setEnabled(!readonlyltem.isSelected()); saveAsAction.setEnabled(!readonlyltem.isSelected()); }
警告:在顯示菜單以前禁用菜單項是一種明智的選擇, 但這種方式不適用於帶有加速鍵的菜單項。這是由於在按下加速鍵時並無打開菜單,所以動做沒有被禁用,導致加速鍵還會觸發這個行爲。
javax.swing.JMenultem 1.2
void setEnabled(boolean b); //啓用或禁用菜單項。
javax.swing.event.MenuListener 1.2
void menuSelected(MenuEvent e); //在菜單被選擇但還沒有打開以前調用。 void menuDeselected(MenuEvent e); //在菜單被取消選擇而且已經關閉以後被調用。 void menuCanceled(MenuEvent e); //當菜單被取消時被調用。例如,用戶點擊菜單之外的區域。
程序清單12-8是建立一組菜單的示例程序。這個程序演示了本節介紹的全部特性,包括: 嵌套菜單、禁用菜單項、複選框和單選鈕菜單項、彈出菜單以及快捷鍵和加速器。
程序清單 12-8 menu/MenuFrame.java
package menu; import java.awt.event.*; import javax.swing.*; /** * A frame with a sample menu bar. */ public class MenuFrame extends JFrame { private static final int DEFAULT_WIDTH = 300; private static final int DEFAULTJEICHT = 200; private Action saveAction; private Action saveAsAction; private JCheckBoxMenuItem readonlyltem; private JPopupMenu popup; /** * A sample action that prints the action name to System.out */ class TestAction extends AbstractAction { public TestAction(String name) { super(name); } public void actionPerformed(ActionEvent event) { System.out.println(getValue(Action.NAME) + " selected."); } } public MenuFrame() { setSize(DEFAULT.WIDTH, DEFAULTJEIGHT); JMenu fileMenu = new JMenu("File"); fileMenu.add(new TestAction("New")); // demonstrate accelerators JMenuItem openltem = fileMenu.add(new TestAction("0pen")); openltem.setAccelerator(KeyStroke.getKeyStroke("ctrl 0")); fileMenu.addSeparator(); saveAction = new TestAction("Save"); JMenuItem saveltem = fileMenu.add(saveAction); saveltem.setAccelerator(KeyStroke.getKeyStroke("ctrl S")); saveAsAction = new TestAction("Save As"); fileMenu.add(saveAsAction); fileMenu.addSeparator(); fileMenu.add(new AbstractAction("Exit") { public void actionPerformed(ActionEvent event) { System,exit(0); } }); // demonstrate checkbox and radio button menus readonlyltem = new JCheckBoxMenuItem("Read-only"); readonlyltem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { boolean saveOk = !readonlyltem.isSelected(); saveAction.setEnabled(saveOk); saveAsAction.setEnabled(saveOk); } }); ButtonCroup group = new ButtonGroup(); JRadioButtonMenuIteni insertltem = new JRadioButtonMenuItem("Insert"); insertltem.setSelected(true); JRadioButtonMenuItem overtypeltem = new JRadioButtonMenuItem("Overtype"); group.add(insertltem); group.add(overtypeltem); // demonstrate icons Action cutAction = new TestAction("Cut"); cutAction.putValue(Action.SMALL_IC0N, new Imagelcon("cut.gif")); Action copyAction = new TestAction("Copy"); copyAction.putValue(Action.SMALL_IC0N, new Imagelcon("copy.gif")); Action pasteAction = new TestAction("Paste"); pasteAction.putValue(Action.SMALL_ICON, new ImageIcon("paste.gif")); JMenu editMenu = new JMenu("Edit"); editMenu.add(cutAction); editMenu.add(copyAction); editMenu.add(pasteAction); // demonstrate nested menus JMenu optionMenu = new JMenu("Options"); optionMenu.add(readonlyltem); optionMenu.addSeparator(); optionMenu.add(insertltem); optionMenu.add(overtypeltem); editMenu.addSeparator(); editMenu.add(optionMenu); // demonstrate mnemonics JMenu helpMenu = new JMenu("Help"); helpMenu.setMnemonic('H'); JMenuItem indexltem = new JMenuItem("Index"); indexltem.setMnemonic(T); helpMenu.add(indexltem); // you can also add the mnemonic key to an action Action aboutAction = new TestAction("About"); aboutAction.putValue(Action.MNEM0NIC_KEY, new Integer('A')); helpMenu.add(aboutAction); // add all top-level menus to menu bar JMenuBar menuBar = new JMenuBar(); setJMenuBar(menuBar); menuBar.add(fileMenu); menuBar.add(editMenu); menuBar.add(helpMenu); // demonstrate pop-ups popup = new JPopupMenu(); popup.add(cutAction); popup.add(copyAction); popup.add(pasteAction); JPanel panel = new JPanel(); panel.setComponentPopupMenu(popup); add(panel); } }
5.7 工具欄
工具欄是在程序中提供的快速訪問經常使用命令的按鈕欄, 如圖 12-24 所示。
工具欄的特殊之處在於能夠將它隨處移動。能夠將它拖拽到框架的四個邊框上,如圖12-25所示。釋放鼠標按鈕後,工具欄將會停靠在新的位置上,如圖12-26所示。
註釋: 工具欄只有位於採用邊框佈局或者任何支持 North、 East、South 和 West 約束佈局管理器的容器內纔可以被拖拽。
工具欄能夠徹底脫離框架。 這樣的工具欄將包含在本身的框架中, 如圖 12-27 所示。當 關閉包含工具欄的框架時, 它會冋到原始的框架中。
編寫建立工具欄的代碼很是容易,而且能夠將組件添加到工具欄中:
JToolBar bar = new JToolBar(); bar.add(blueButton);
JToolBar類還有一個用來添加Action對象的方法,能夠用Action對象填充工具欄:
bar.add(blueAction);
這個動做的小圖標將會出如今工具欄中。能夠用分隔符將按鈕分組:
bar.addSeparator();
例如,圖12-24中的工具欄有一個分隔符,它位於第三個按鈕和第四個按鈕之間。而後,將工具欄添加到框架中:
add(bar, BorderLayout.NORTH);
當工具欄沒有停靠時,能夠指定工具欄的標題:
bar = new JToolBar(titleString);
在默認狀況下,工具欄最初爲水平的。若是想要將工具欄垂直放置,可使用下列代碼:
bar = new JToolBar(SwingConstants.VERTICAL)
或者
bar = new JToolBar(titleString, SwingConstants.VERTICAL)
按鈕是工具欄中最多見的組件類型。然而工具欄中的組件並不只限如此。例如,能夠往工具欄中加入組合框。
5.8 工具提示
工具欄有一個缺點,這就是用戶經常須要猜想按鈕上小圖標按鈕的含義。爲了解決這個問題,用戶界面設計者發明了工具提示(tooltips)。當光標停留在某個按鈕上片刻時,工具提示就會被激活。工具提示文本顯示在一個有顏色的矩形裏。 當用戶移開鼠標時,工具提示就會自動地消失。如圖12-28所示。
在Swing中,能夠調用setToolText方法將工具提不添加到Component上:
exitButton.setToolTipText("Exit");
還有一種方法是,若是使用Action對象,就能夠用SHORT_DESCRIPTION關聯工具提示:
exitAction.putValue(Action.SHORTJESCRIPnON, "Exit");
程序清單12-9說明了如何將一個Action對象添加到菜單和工具欄中。注意,動做名在菜單中就是菜單項名,而在工具欄中就是簡短的說明。
程序清單 12-9 tooBar/TooBarFrame.java
package toolBar; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * A frame with a toolbar and menu for color changes. */ public class ToolBarFrame extends JFrame { private static final int DEFAULT_WIDTH = 300; private static final int DEFAULT_HEIGHT = 200; private JPanel panel; public ToolBarFrame() { setSize(DEFAULT_WIDTH, DEFAULTJEICHT); // add a panel for color change panel = new JPanel(); add(panel, BorderLayout.CENTER); // set up actions Action blueAction = new ColorAction("Blue", new Imagelcon("blue-ball.gif"), Color.BLUE); Action yellowAction = new ColorAction("Yellow", new Imagelcon("yel1ow-bal1.gif"),Color.YELLOW); Action redAction = new ColorAction("Red", new Imagelcon("red-ball,gif"),Color.RED); Action exitAction = new AbstractAction("Exit", new Imagelcon("exit,gif")) { public void actionPerformed(ActionEvent event) { System.exit(0); } }; exitAction.putValue(Action.SH0RT_JESCRIPTI0N, "Exit"); // populate toolbar JToolBar bar = new JToolBar(); bar.add(blueAction); bar.add(yellowAction); bar.add(redAction); bar.addSeparator(); bar.add(exitAction); add(bar, BorderLayout.NORTH); // populate menu JMenu menu = new JMenu("Color"); menu.add(yellowAction); menu.add(blueAction); menu.add(redAction); menu.add(exitAction); JMenuBar menuBar = new JMenuBar(); menuBar.add(menu); setjMenuBar(menuBar); } /** * The color action sets the background of the frame to a given color. */ class ColorAction extends AbstractAction { public ColorAction(String name, Icon icon, Color c) { putValue(Action.NAME, name); putValue(Action.SMALL_ICON, icon); putValue(Action.SHORTJESCRIPTION, name + " background"); putValue("Color", c); } public void actionPerformed(ActionEvent event) { Color c = (Color) getValue("Color"); panel.setBackground(c); } } }
javax.swing.JToolBar 1.2
JToolBar(); JToolBar(String titlestring); JToolBar(int orientation); JToolBar(String titeString, int orientation); //用給定的標題字符串和方向構造一個工具欄。Orientation能夠是SwingConstants.HORIZONTAL (默認)或SwingConstants.VERTICAL。 JButton add(Action a); //用給定的動做名、圖標、簡要的說明和動做回調構造一個工具欄中的新按鈕。 void addSeparator(); //將一個分隔符添加到工具欄的尾部。
javax.swing.JComponent 1.2
void setToolTipText(String text); //設置當鼠標停留在組件上時顯示在工具提示中的文本
6 複雜的佈局管理
7 對話框
到目前爲止,全部的用戶界面組件都顯示在應用程序建立的框架窗口中。這對於編寫運行在Web瀏覽器中的applets來講是十分常見的狀況。可是,若是編寫應用程序,一般就須要彈出獨立的對話框來顯示信息或者獲取用戶信息。
與大多數的窗口系統同樣,AWT也分爲模式對話框和無模式對話框。所謂模式對話框是指在結束對它的處理以前,不容許用戶與應用程序的其他窗口進行交互。模式對話框主要用於在程序繼續運行以前獲取用戶提供的信息。例如,當用戶想要讀取文件時,就會彈出一個模式對話框。用戶必須給定一個文件名,而後程序纔可以開始讀操做。只有用戶關閉(模式) 對話框以後, 應用程序纔可以繼續執行。
所謂無模式對話框是指容許用戶同時在對話框和應用程序的其餘窗口中輸入信息。使用無模式對話框的最好例子就是工具欄。工具欄能夠停靠在任何地方,而且用戶能夠在須要的時候,同時與應用程序窗口和工具欄進行交互。
本節從最簡單的對話框開始個簡單信息的模式對話框。Swing有一個很容易使用的類JOptionPane,它能夠彈出一個簡單的對話框,而沒必要編寫任何對話框的相關代碼。隨後,將看到如何經過實現本身的對話框窗口來編寫一個複雜的對話框。最後,介紹在應用程序與對話框之間如何傳遞數據。
本節用兩個標準的對話框結束:文件對話框和顏色對話框。文件對話框比較複雜,爲此須要熟悉Swing中的JFileChooser自已編寫文件對話框是一項很有挑戰性的任務。JColorChooser對話框可用來讓用戶選取顏色。
7.1 選項對話框
Swing有一套簡單的對話框,用於獲取用戶的一些簡單信息。JOptionPane有4個用於顯示這些對話框的靜態方法:
showMessageDialog: 顯示一條消息並等待用戶點擊OK showConfirmDialog: 顯示一條消息並等待用戶確認(與 OK/Cancel相似) showOptionDialog: 顯示一條消息並得到用戶在一組選項中的選擇 showInputDialog: 顯示一條消息並得到用戶輸入的一行文本
圖12-36顯示了一個典型的對話框。能夠看到,對話框有下列組件:
- 一個圖標
- 一條消息
- 一個或多個按鈕
輸入對話框有一個用於接收用戶輸入的額外組件。它既多是用於輸入任何字符串的文本域,也多是容許用戶從中選擇的組合框。
這些對話框的確切佈局和爲標準消息類型選擇的圖標都取決於具體的觀感。
左側的圖標將由下面5種消息類型決定:
ERROR_MESSAGE INFORMATION_MESSAGE WARNINC_MESSAGE QUESTION_MESSAGE PLAIN_MESSAGE
PLAIN_MESSAGE類型沒有圖標。每一個對話框類型都有一個方法,能夠用來提供本身的圖標,以替代原來的圖標。
能夠爲每一個對話框類型指定一條消息。這裏的消息既能夠是字符串、圖標、用戶界面組件,也能夠是其餘類型的對象。下面是顯示消息對象的基本方式:
String: 繪製字符串 Icon:顯示圖標 Component: 顯示組件 Object[]: 顯示數組中的全部對象,依次疊加 任何其餘對象: 調用 toString 方法來顯示結果字符串
能夠運行程序清單12-15中的程序,查看一下這些選項。
固然,提供字符串消息是最多見的狀況,而提供一個Component會帶來更大的靈活性。這是由於經過調用paintComponent方法能夠繪製本身想要的任何內容。
位於底部的按鈕取決於對話框類型和選項類型。當調用showMessageDialog和showInputDialog時,只能看到一組標準按鈕(分別是OK/Cancel)。當調用showConfirmDialog時,能夠選擇下面四種選項類型之一:
DEFAUL_OPTI0N YES_NO_OPTION YES_N0_CANCEL_OPTI0N OK_CANCEL_OPTION
使用showOptionDialog能夠指定任意的選項。這裏須要爲選項提供一個對象數組。每一個數組元素能夠是下列類型之一:
String: 使用字符串標籤建立一個按鈕 Icon: 使用圖標建立一個按鈕 Component: 顯示這個組件
其餘類型的對象:使用toString方法,而後用結果字符串做爲標籤建立按鈕
下面是這些方法的返回值:
showMessageDialog 無 showConfirmDialog 表示被選項的一個整數 showOptionDialog 表示被選項的一個整數 showInputDialog 用戶選擇或輸入的字符串
showConfirmDialog和showOptionDialog返回一個整數用來表示用戶選擇了哪一個按鈕。對於選項對話框來講,這個值就是被選的選項的索引值或者是CLOSED_OPTION (此時用戶沒有選擇可選項, 而是關閉了對話框)。對於確認對話框,返回值能夠是下列值之一:
OK_OPTION CANCEL_OPTI0N YES_OPTION N0_OPTION CLOSED_OPTION
這些選項彷佛使人感到疑惑不解,實際上很是簡單步驟以下:
- 1 ) 選擇對話框的類型(消息、確認、選項或者輸入)。
- 2 ) 選擇圖標(錯誤、信息、警告、問題、無或者自定義)。
- 3 ) 選擇消息(字符串、圖表、 自定義組件或者它們的集合)。
- 4 ) 對於確認對話框,選擇選項類型 (默認、Yes/No、Yes/No/Cancel或者Ok/Cancel)。
- 5 ) 對於選項對話框,選擇選項(字符串、圖表或者自定義組件)和默認選項。
- 6 ) 對於輸入對話框,選擇文本框或者組合框。
- 7 ) 調用JOptionPane API中的相應方法。
例如, 假設須要顯示一個圖12-36的對話框。這個對話框顯示了一條消息,並請求用戶確認或者取消。這是一個確認對話框。圖標是警告圖標,消息是字符串,選項類型是OK_CANCEL_OPTION。調用以下:
int selection = JOptionPane.showConfirfnDialog(parent, "Message", "Title", JOptionPane.OK_CANCEL_OPTI0N, JOptionPane.QUESTION_MESSACE); if (selection == JOptionPane.OK_0PTI0N) ...
提示: 消息字符串中能夠包含換行符('\n')。這樣就可讓字符串多行顯示;
這個程序顯示了6個按鈕面板(見圖 12-37 ), 其框架類在程序清單12-15中給出。程序清單12-16顯示了這些面板的類。點擊Show按鈕時,會顯示所選的對話框。
程序清單 12-15 optionDialog/OptionDialogFrame.java
package optionDialog; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.util.*; import javax.swing.*; /** * A frame that contains settings for selecting various option dialogs. */ public class OptionDialogFrame extends JFrame { private ButtonPanel typePanel: private ButtonPanel messagePanel; private ButtonPanel messageTypePanel; private ButtonPanel optionTypePanel; private ButtonPanel optionsPanel; private ButtonPanel inputPanel; private String messagestring = "Message"; private Icon messageIcon = new ImageIon("blue-ball.gif"); private Object messageObject = new Date(); private Component messageComponent = new SampleComponent(); public OptionDialogFrame() { JPanel gridPanel = new JPanel(); gridPanel.setLayout(new GridLayout(2, 3)); typePanel = new ButtonPanel ("Type", "Message", "Confirm", "Option", "Input"); messageTypePanel = new ButtonPanel ("Message Type", "ERROR_MESSAGE", "INFORMATION_MESSACE", "WARNIN_MESSAGE", "QUESTION_MESSAGE", "PLAIN_MESSAGE"); messagePanel = new ButtonPanel ("Message", "String", "Icon", "Component", "Other", "Object[]"); optionTypePanel = new ButtonPanel ("Confirm", "DEFAULT_0PTI0N", "YES_N0_0PTI0N", "YES_NO_CANCEL.OPTION", "OK_CANCEL_0PTI0N"); optionsPanel = new ButtonPanel ("Option", "String[]", "Icon[]","Object[]"); inputPanel = new ButtonPanel ("Input", "Text field", "Combo box"); gridPanel.add(typePanel); gridPanel.add(messageTypePanel); gridPanel.add(messagePanel); gridPanel.add(optionTypePanel); gridPanel.add(optionsPanel); gridPanel.add(inputPanel); // add a panel with a Show button JPanel showPanel = new JPanel(); JButton showButton = new JButton("Show"); showButton.addActionListener(new ShowAction()); showPanel.add(showButton); add(gridPanel, BorderLayout.CENTER); add(showPanel, BorderLayout.SOUTH); pack(); } /** * Gets the currently selected message. ©return a string, icon, component, or object array, depending on the Message panel selection */ public Object getMessage() { String s = messagePanel.getSelection(); if (s.equals("String")) return messageString; else if (s.equals("Icon")) return messagelcon; else if (s.equals("Component")) return messageComponent; else if (s.equals("Object[]")) return new Object[] { messagestring, messagelcon, messageComponent, messageObject }; else if (s.equals("Other")) return messageObject; else return null; } /** * Gets the currently selected options. * ©return an array of strings, icons, or objects, depending on the Option panel selection */ public Object[] getOptions() { String s = optionsPanel.getSelection(); if (s.equals("String[]")) return new String[] {"Yellow", "Blue", "Red"}; else if (s.equals("Icon[]")) return new Icon[] { new Imagelcon("yellow-ball.gif"), new "ImageIconrblue-ball.gif"), new Imagelcon("red-bal1.gif") }; else if (s.equals("Object[]")) return new Object[] { messageString, messagelcon, messageComponent, messageObject }; else return null; } /** * Gets the selected message or option type * @param panel the Message Type or Confirm panel * @return the selected XXX MESSAGE or XXX.OPTION constant from the JOptionPane class */ public int getType(ButtonPanel panel) { String s = panel.getSelection(); try { return JOptionPane.class.getField(s).getlnt(null); } catch (Exception e) { return -1; } } /** * The action listener for the Show button shows a Confirm, Input, Message, or Option dialog * depending on the Type panel selection. */ private class ShowAction implements ActionListener { public void actionPerformed(ActionEvent event) { if (typePanel.getSelection().equals("Confirm")) JOptionPane.showConfirmDialog(OptionDialogFrame.this, getMessage(), "Title", getType(optionTypePanel), getType(messageTypePanel)); else if (typePanel.getSelection().equals("Input")) { if (inputPanel.getSelection().equals("Text field")) JOptionPane.showInputDialog(OptionDialogFrame.this, getMessage(), "Title", getType(messageTypePanel)); else JOptionPane.showInputDialog(OptionDialogFrame.this, getMessageQ, "Title",getType(messageTypePanel), null, new String[] { "Yellow", "Blue", "Red" }, "Blue"); } else if(typePanel.getSelection().equals("Message")) JOptionPane.showMessageDialog(OptionDialogFrame.this, getMessage(), "Title", getType(messageTypePanel)); else if (typePanel.getSelection().equals("Option")) JOptionPane.showOptionDialog(OptionDialogFrame,this, getMessage(), "Title", getType(optionTypePanel), getType(messageTypePanel), null, getOptions(), getOptions()[0]); } } } /** * A component with a painted surface */ class SampleComponent extends JComponent { public void paintComponent(Graphics g) { Craphics2D g2 = (Graphics2D) g; Rectangle2D rect = new Rectangle2D.Double(0, 0, getWidth() - 1 , getHeight() - 1 ); g2.setPaint(Color.YELLOW); g2.fill(rect); g2.setPaint(Color.BLUE); g2.draw(rect); } public Dimension getPreferredSize() { return new Dimension(10, 10); } }
程序清單 12-16 optionDialog/ButtonPanel.java
package optionDialog; import javax.swing.*; /** * A panel with radio buttons inside a titled border. */ public class ButtonPanel extends JPanel { private ButtonGroup group; /** * Constructs a button panel. * @param title the title shown in the border is * @param options an array of radio button labels 16 */ public ButtonPanel(String title, String... options) { setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), title)); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); group = new ButtonGroup(); // make one radio button for each option for (String option : options) { JRadioButton b = new JRadioButton(option); b.setActionCommand(option); add(b); group.add(b); b.setSelected(option == options[0]); } } /** * Gets the currently selected option. * ©return the label of the currently selected radio button. */ public String getSelection() { return group.getSelection().getActionCommand(); } }
javax.swing.JOptionPane 1.2
static void showMessageDialog(Component parent, Object message, String title, int messageType, Icon icon); static void showMessageDialog(Component parent, Object message, String title, int messageType); static void showMessageDialog(Component parent, Object message); static void showlnternalMessageDialog(Component parent, Object message, String title, int messageType, Icon icon); static void showlnternalMessageDialog(Component parent, Object message, String title, int messageType); static void showlnternalMessageDialog(Component parent, Object message); /* 顯示一個個消息對話框或者一個內部消息對話框(內部對話框徹底顯示在所在的框架內)。 參數: parent 父組件(能夠爲 null)。 message 顯示在對話框中的消息(能夠是字符串、圖標、 組件或者一個這些類型的數組)。 title 對話框標題欄中的字符串。 messageType 取值爲 ERROR_MESSAGE、 INFORMATION_MESSAGE、 WARNING_MESSAGE、 QUESTION_MESSAGE、 PLAIN_MESSAGE 之一。 icon 用於替代標準圖標的圖標。 */ static int showConfirmDialog(Component parent, Object message, String title, int optionType, int messageType, Icon Icon); static int showConfirmDialog(Component parent, Object message, String title, int optionType, int messageType); static int showConfirmDialog(Component parent, Object message, String title, int optionType); static int showConfirmDialog(Component parent, Object message); static int showInternalConfirmDialog(Component parent, Object message, String title, int optionType, int messageType, Icon icon); static int showInternalConfirmDialog(Component parent, Object message, String title, int optionType, int messageType); static int showInternalConfirmDialog(Component parent, Object message, String title, int optionType); static int showlnternalConfirmDialog(Component parent, Object message); /* 顯示一個確認對話框或者內部確認對話框(內部對話框徹底顯示在所在的框架內)。返冋用戶選擇的選項(取值爲 OK_OPTION, CANCEL_OPTION, YES_OPTION, NO_OPTION);若是用戶關閉對話框將返回 CLOSED_OPTION。 參數:parent 父組件 (能夠爲 null)。 message 顯示在對話框中的消息(能夠是字符串、 圖標、 組件或者一個這些類型的數組)。 title 對話框標題欄中的字符串。 messageType 取值爲 ERROR_MESSAGE、 INFORMAT10N_MESSAGE、 WARNING_MESSAGE、QUESTION_MESSAGE、 PLAIN_MESSAGE 之一 optionType 取值爲 DEFAULT_OPTION、YES—N0_0PT10N、 YES_NO CANCEL— OPTION、OK_CANCEL_OPTION 之一。 icon 用於替代標準圖標的圖標。 */ static int showOptionDialog(Component parent, Object message, String title, int optionType, int messageType, Icon icon, Object[] options, Object default); static int showlnternalOptionDialog(Component parent, Object message, String title, int optionType, int messageType, Icon icon, Object[]options, Object default); /* 顯示一個選項對話框或者內部選項對話框(內部對話框徹底顯示在所在的框架內)。返回用戶選擇的選項索引;若是用戶取消對話框返冋 CLOSED_OPTION。 參數:parent 父組件(能夠爲 null)。 message 顯示在對話框中的消息(能夠是字符串,圖標,組件或者一個這些類型的數組) 。 title 對話框標題欄中的字符串。 messageType 取值爲 ERROR_MESSAGE、 INFORMATION_MESSAGE、 WARNING_MESSAGE、QUESTION_MESSAGE、 PLAIN_MESSAGE 之一 optionType 取值爲 DEFAULT_0PT10N、YES_NO_OPTION、 YES_NO_ CANCEL_OPTION、 OK_CANCEL_OPTION 之一。 icon 用於替代標準圖標的圖標。 options 一組選項(能夠是字符串、 圖標或者組件)。 default 呈現給用戶的默認值。 */ static Object showInputDialog(Component parent, Object message, String title, int messageType, Icon icon, Object[] values, Object default); static String showInputDialog(Component parent, Object message, String title, int messageType); static String showInputDialog(Component parent, Object message); static String showInputDialog(Object message); static String showInputDialog(Component parent, Object message, Object default) 1.4 static String showInputDialog(Object message, Object default) 1.4 static Object showinternalInputDialog(Component parent, Object message, String title, int messageType, Icon icon, Object[] values, Object default); static String showinternalInputDialog(Component parent, Object message, String title, int messageType); static String showinternalInputDialog(Component parent, Object message); /* 顯示一個輸入對話框或者內部輸入對話框(內部對話框徹底顯示在所在的框架內)。返回用戶輸入的字符串; 若是用戶取消對話框返回 null。 參數:parent 父組件(能夠爲 null)。 message 顯示在對話框中的消息(能夠是字符串、 圖標、 組件或者一 個這些類型的數組)。 title 對話框標題欄中的字符串。 messageType 取值爲 ERROR_MESSAGE、INFORMATION_MESSAGE , WARNING—MESSAGE、 QUESTION_MESSAGE、 PLAIN_ MESSAGE 之一。 icon 用於替代標準圖標的圖標。 values 在組合框中顯示的一組值。 default 呈現給用戶的默認值。 */
7.2 建立對話框
在上一節中,介紹瞭如何使用JOptionPane來顯示一個簡單的對話框。本節將講述如何手工地建立這樣一個對話框。
圖 12-38 顯示了一個典型的模式對話框。當用戶點擊About按鈕時就會顯示這樣一個程序信息對話框。
要想實現一個對話框,須要從JDialog派生一個類。這與應用程序窗口派生於JFrame的過程徹底同樣。具體過程以下:
- 1 ) 在對話框構造器中,調用超類JDialog的構造器。
- 2 ) 添加對話框的用戶界面組件。
- 3 ) 添加事件處理器。
- 4 ) 設置對話框的大小。
在調用超類構造器時,須要提供擁有者框架(ownerframe)、對話框標題及模式特徵。
擁有者框架控制對話框的顯示位置,若是將擁有者標識爲null, 那麼對話框將由一個隱藏框架所擁有。
模式特徵將指定對話框處於顯示狀態時,應用程序中其餘窗口是否被鎖住。無模式對話框不會鎖住其餘窗口,而有模式對話框將鎖住應用程序中的全部其餘窗口(除對話框的子窗口外)。用戶常用的工具欄就是無模式對話框,另外一方面,若是想強迫用戶在繼續操做以前提供一些必要的信息就應該使用模式對話框。
註釋:在Java SE 6中,有兩個額外的模式特徵類型。文檔-模式對話框將阻塞全部屬於相同「文檔」 的窗口。更準確地說,是全部做爲對話框的具備相同無父根窗口的窗口。這樣解決了幫助系統的問題。在早期的版本中,當彈出一個模式對話框時, 用戶不可能與幫助窗口交互。工具箱對話框阻塞了全部來自相同「工具箱」的窗口。工具箱是一個 運行於多個應用的Java程序,例如,瀏覽器中的applet引擎。有關更加詳細的內容請參看網站:www.oracle.com/technetwork/artides/javase/modality-137604.html。
下面是一個對話框的例子:
public AboutDialog extends JDialog { public AboutDialog(JFrame owner) { super(owner, "About DialogTest", true); add(new JLabel("<html><h1><i>Core Java</i></h1><hr>By Cay Horstmann</html>"),BorderLayout.CENTER); JPanel panel = new JFanel(); JButton ok = new JButton("OK"); ok.addActionListener(event -> setVisible(false)); panel.add(ok); add(panel, BorderLayout.SOUTH); setSize(250, 150); } }
正如看到的,構造器添加了用戶界面組件,在本例中添加的是標籤和按鈕,而且爲按鈕設置了處理器,而後還設置了對話框的大小。
要想顯示對話框,須要創建一個新的對話框對象, 並讓它可見:
JDialog dialog = new AboutDialog(this); dialog.setVisible(true);
實際上,在下面的示例代碼中,只創建了一次對話框,不管什麼時候用戶點擊About按鈕,均可以重複使用它。
if (dialog == null) // first time dialog = new AboutDialog(this); dialog.setVisible(true);
當用戶點擊OK按鈕時,該對話框將被關閉。下面是在OK按鈕的事件處理器中的處理代碼:
ok.addActionListener(event -> setVisible(false));
當用戶點擊Close按鈕關閉對話框時,對話框就被隱藏起來。與JFrame—樣,能夠覆蓋 setDefaultCloseOperation方法來改變這個行爲。 程序清單12-17是測試程序框架類的代碼。程序清單12-18顯示了對話框類。
程序清單12-17dialog/DialogFrame.java
package dialog; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; /** * A frame with a menu whose File->About action shows a dialog. */ public class DialogFrame extends JFrame { private static final int DEFAULT_WIDTH = 300; private static final int DEFAULT_HEICHT = 200; private AboutDialog dialog; public DialogFrame() { setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); // Construct a File menu. JMenuBar menuBar = new JMenuBar(); setDMenuBar(menuBar); JMenu fileMenu = new JMenu("File"); menuBar.add(fileMenu); // Add About and Exit menu items. // The About item shows the About dialog. JMenuItem aboutItem = new JMenuItem("About"); aboutItem.addActionListener(event -> { if (dialog = null) // first time dialog = new AboutDialog(DialogFrame.this); dialog.setVisible(true); // pop up dialog }); fileMenu.add(aboutItem); // The Exit item exits the program. JMenuItem exitItem = new JMenuItem("Exit"); exitltem.addActionListener(event -> System.exit(O)); fileMenu.add(exitItem); } }程序清單 12-18 dialog/AboutDialog.java 1
package dialog; import java.awt.BorderLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; /** * sample modal dialog that displays a message and waits for the user to click the OK button. */ public class AboutDialog extends JDialog { public AboutDialog(JFrame owner) { super(owner,"About DialogTest", true); // add HTML label to center add( new JLabel( "<html><hl><i>Core Java</i></h1><hr>By Cay Horstmann</html>"), BorderLayout.CENTER); // OK button closes the dialog JButton ok = new JButton("OK"); ok.addActionListener(event -> setVisible(false)); // add OK button to southern border JPanel panel = new JPanel(); panel.add(ok); add(panel, BorderLayout.SOUTH); pack(); } }
javax.swing.JDialog 1.2
public JDialog(Frame parent, String title, boolean modal); //構造一個對話框。在沒有明確地讓對話框顯示以前, 它是不可見的。 //參數:parent 對話框擁有者的框架 //title 對話框的標題 //modal true表明模式對話框(模式對話框阻塞其餘窗口的輸入)
7.3 數據交換
使用對話框最一般的目的是獲取用戶的輸入信息。在前面已經看到,構造對話框對象很是簡單:首先初始化數據,而後調用setViSible(true)就會在屏幕上顯示對話框。如今,看看如何將數據傳入傳出對話框。
看一下如圖12-39所示的對話框,能夠用來得到用戶名和用戶密碼以便鏈接某些在線服務。
對話框應該提供設置默認數據的方法。例如,示例程序中的PasswordChooser類提供了一個setUser方法,用來將默認值放到下面的字段中:
public void setUser(User u) { username.setText(u.getName()); }
一旦設置了默認值(若是須要),就能夠調用setViSible(true)讓對話框顯示在屏幕上。
而後用戶輸入信息,點擊OK或者Cancel按鈕。這兩個按鈕的事件處理器都會調用SetVisible(false)終止對 setVisible(true) 的調用。另外,用戶也能夠選擇關閉對話框。若是沒有爲對話框安裝窗口監聽器,就會執行默認的窗口結束操做,即對話框變爲不可見,這也停止了對setVisible(true)的調用。
重要的問題是在用戶解除這個對話框以前,一直調用setVisible(true)阻塞。這樣易於實現模式對話框。
但願知道用戶是接收對話框,仍是取消對話框。在示例代碼中設置了OK標誌,在對話框顯示以前是false。只有OK按鈕的事件處理器能夠將它設置爲true。這樣,就能夠得到對話框中的用戶輸入。
註釋:無模式對話框數據傳輸就沒有那麼簡單了。當無模式對話框顯示時,調用setVisible(true) 並不阻塞,在對話框顯示時,其餘程序仍繼續運行。若是用戶選擇了無模式對話框中的一項,並點擊OK, 對話框就會將一個事件發送給程序中的某個監聽器。
示例程序中還包含另一個頗有用的改進。在構造一個JDialog對象時,須要指定擁有者框架。可是,在不少狀況下,一個對話框可能會有多個擁有者框架,因此最好在準備顯示對話框時再肯定擁有者框架,而不是在構造PasswordChooser對象時。
有一個技巧是讓PasswordChooser擴展JPanel, 而不是擴展JDialog, 在showDialog方法中動態創建JDialog對象:
public boolean showDialog(Frame owner, String title) { ok = false; if (dialog = null || dialog.getOwner()!= owner) { dialog = new JDialog(owner, true); dialog.add(this); dialog.pack(); } dialog.setTitie(title); dialog.setVisible(true); return ok; }
注意,讓owner等於null是安全的。
能夠再作進一步的改進。有時,擁有者框架並不老是可用的。利用任意的parent組件能夠很容易地獲得它。以下所示:
Frame owner; if (parent instanceof Frame) owner = (Frame) parent; else owner = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent);
在示例程序中使用了改進的代碼。JOptionPane類也使用了上面的機制。
不少對話框都有默認按鈕。 若是用戶按下一個觸發器鍵(在大多數「觀感」 實現中是ENTER) 就自動地選擇了它。默認按鈕一般用加粗的輪廓給予特別標識。
能夠在對話框的根窗格(root pane) 中設置默認按鈕:
dialog.getRootPane().setDefaultButton(okButton);
若是按照前面的建議,在一個面板中佈局對話框就必須特別當心。在包裝面板進入對話框後再設置默認按鈕。面板自己沒有根窗格。
程序清單12-19是程序的框架類,這個程序展現了進出對話框的數據流。程序清單12-20給出了對話框類。
程序清單12-19 dataExchange/DataExchangeFrame.java
package dataExchange; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * A frame with a menu whose File->Connect action shows a password dialog. */ public class DataExchangeFrame extends JFrame { public static final int TEXT_ROWS = 20; public static final int TEXT_C0LUMNS = 40; private PasswordChooser dialog = null; private JTextArea textArea; public DataExchangeFrame() { // construct a File menu JMenuBar mbar = new JMenuBar(); setJMenuBar(mbar); JMenu fileMenu = new JMenu("File"); mbar.add(fileMenu); // add Connect and Exit menu items JMenuItem connectltem = new JMenuItem("Connect"); connectltem.addActionListener(new ConnectAction()); fileMenu.add(connectltem); // The Exit item exits the program JMenuItem exitltem = new JMenuItem("Exit"); exitltem.addActionListener(event -> System.exit(0)); fileMenu.add(exitltem); textArea = new JTextArea(TEXT_R0WS, TEXT_C0LUMNS); add(new JScrollPane(textArea), BorderLayout.CENTER); pack(); } /** * The Connect action pops up the password dialog. */ private class ConnectAction implements ActionListener { public void actionPerformed(ActionEvent event) { // if first time, construct dialog if (dialog == null) dialog = new PasswordChooser(); // set default values dialog.setUser(new User("yourname", null)); // pop up dialog if (dialog.showDialog(DataExchangeFrame.this, "Connect")) { // i f accepted, retrieve user input User u = dialog.getUser(); textArea.appendfuser name = " + u.getName() + ", password = + (new String(u.getPassword())) + "\n"); } } } }
程序清單 12-20 dataExchange/PasswordChooser.java
package dataExchange; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Frame; import java.awt.GridLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import :javax.swing.JPasswordField; import javax.swingJTextField; import javax.swing.SwingUti1ities; /** * A password chooser that is shown inside a dialog */ public class PasswordChooser extends JPanel { private JTextField username; private JPasswordField password; private JButton okButton; private boolean ok; private JDialog dialog; public PasswordChooser() { setLayout(new BorderLayout()); // construct a panel with user name and password fields JPanel panel = new JPanel(); panel.setLayout(new CridLayout(2, 2)); panel.add(new JLabel("User name:")); panel.add(username = new JTextField("")); panel.add(new JLabel("Password:")); panel.add(password = new JPasswordField("")); add(panel, BorderLayout.CENTER); // create Ok and Cancel buttons that terminate the dialog okButton = new JButton("Ok"); okButton.addActionListener(event -> { ok = true; dialog.setVisible(false); }); JButton cancelButton = new JButton("Cancel"); cancelButton.addActionListener(event -> dialog.setVisible(false)); // add buttons to southern border JPanel buttonPanel = new JPanel(); buttonPanel.add(okButton); buttonPanel.add(cancelButton); add(buttonPanel, BorderLayout.SOUTH); } /** * Sets the dialog defaults. * @param u the default user information */ public void setUser(User u) { username.setText(u.getName()); } /** * Gets the dialog entries. * ©return a User object whose state represents the dialog entries */ public User getUser() { return new User(username.getText(), password.getPassword()); } /** * Show the chooser panel in a dialog * @param parent a component in the owner frame or null * @param title the dialog window title */ public boolean showDialog(Component parent, String title) { ok = false; // locate the owner frame Frame owner = null; if (parent instanceof Frame) owner = (Frame) parent; else owner = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent); // if first time, or if owner has changed, make new dialog if (dialog = null || dialog.getOwner() != owner) { dialog = new Dialog(owner, true); dialog.add(this); dialog.getRootPane().setDefaultButton(okButton); dialog.pack(); } // set title and show dialog dialog.setTitie(title); dialog.setVisible(true); return ok; } }
javax.swing.SwingUtilities 1.2
Container getAncestorOfClass(Class c, Component comp); //返回給定組件的最早的父容器。這個組件屬於給定的類或者其子類之一。
javax.swing.JComponent 1.2
JRootPane getRootPane(); //得到最靠近這個組件的根窗格,若是這個組件的祖先沒有根窗格返回 null。
javax.swing.JRootPane 1.2
void setDefaultButton(JButton button); //設置根窗格的默認按鈕。要想禁用默認按鈕,須要將參數設置爲null。
javax.swing.JButton 1.2
boolean isDefaultButton(); //若是這個按鈕是它的根窗格的默認按鈕,返回true。
7.4 文件對話框
當編寫應用程序時,一般但願能夠打開和保存文件。一個好的文件對話框應該能夠顯示文件和目錄,可讓用戶瀏覽文件系統,這是很難編寫的。人們確定不肯意從頭作起。很幸運,Swing中提供了JFileChooser類,它能夠顯示一個文件對話框,其外觀與本地應用程序中使用的文件對話框基本同樣。JFileChooser是一個模式對話框。注意,JFileChooser類並非JDialog類的子類。須要調用showOpenDialog,而不是調用SetVisible(true)顯示打開文件的對話框,或者調用showSaveDialog顯示保存文件的對話框。接收文件的按鈕被自動地標籤爲Open或者Save。也能夠調用showDialog方法爲按鈕設定標籤。圖12*40是文件選擇對話框的樣例。
下面是創建文件對話框而且獲取用戶選擇信息的步驟:
- 1 ) 創建一個JFileChooser對象。與JDialog類的構造器不一樣,它不須要指定父組件。容許在多個框架中重用一個文件選擇器。例如:
JFileChooser chooser = new JFileChooser();
提示:重用一個文件選擇器對象是一個很好的想法,其緣由是JFileChooser的構造器至關耗費時間。特別是在Windows上,用戶映射了不少網絡驅動器的狀況下。
- 2 ) 調用setCurrentDirectory方法設置當前目錄。例如,使用當前的工做目錄:
chooser.setCurrentDirectory(new File("."));
須要提供一個File對象。File對象將在卷II的第2章中詳細地介紹。這裏只須要知道構造器File (String fileName) 可以將一個文件或目錄名轉化爲一個File對象便可。
- 3 ) 若是有一個想要做爲用戶選擇的默認文件名,可使用setSelectedFile方法進行指定:
chooser.setSelectedFile(new File(filename));
- 4 ) 若是容許用戶在對話框中選擇多個文件,須要調用setMultiSelectionEnabled方法。 固然,這是可選的。
chooser.setMultiSelectionEnabled(true);
-
5 ) 若是想讓對話框僅顯示某一種類型的文件(如,全部擴展名爲.gif 的文件),須要設置文件過濾器,稍後將會進行討論。
-
6 ) 在默認狀況下,用戶在文件選擇器中只能選擇文件。若是但願選擇目錄,須要調用 setFileSelectionMode方法。參數值爲:JFileChooser.FILES_ONLY (默認值),JFileChooser.DIRECTORIES_ONLY或者JFileChooser.FILES_AND_DIRECTORIES
-
7 ) 調用 showOpenDialog或者showSaveDialog方法顯示對話框。必須爲這些調用提供父組件:
int result = chooser.showOpenDialog(parent);
或者
int result = chooser.showSaveDialog(parent);
這些調用的區別是「確認按鈕」的標籤不一樣。點擊「確認按鈕」將完成文件選擇。也能夠調用showDialog方法,並將一個顯式的文本傳遞給確認按鈕:
int result = chooser.showDialog(parent, "Select");
僅當用戶確認、取消或者離開對話框時才返回調用。返回值能夠是JFileChooser.APPROVE_OPTION、JFileChooser.CANCEL_OPTION或者 JFileChooser.ERROR_OPTION。
- 8 ) 調用
getSelectedFile()
或者getSelectedFiles()
方法獲取用戶選擇的一個或多個文件。這些方法將返回一個文件對象或者一組文件對象。若是須要知道文件對象名時,能夠調用getPath方法。例如:
String filename = chooser.getSelectedFile().getPath();
在大多數狀況下,這些過程比較簡單。使用文件對話框的主要困難在於指定用戶須要選擇的文件子集。例如,假定用戶應該選擇GIF圖像文件。後面的文件選擇器就應該只顯示擴展名爲.gif的文件,而且,還應該爲用戶提供反饋信息來講明顯示的特定文件類別,如「GIF圖像」。然而,狀況有可能會更加複雜。若是用戶應該選擇JPFG圖像文件,擴展名就能夠是.jpg或者.jpeg。與從新編碼實現這種複雜狀況相比,文件選擇器的設計者提供了一種更好的機制:若想限制顯示的文件,須要建立一個實現了抽象類javax.swing.filechooser.FileFilter的對象。文件選擇器將每一個文件傳遞給文件過濾器,只有文件過濾器接受的文件才被最終顯示出來。
在編寫本書的時候,有兩個子類可用:能夠接受全部文件的默認過濾器和能夠接受給定擴展名的全部文件的過濾器。其實,設計專用文件過濾器很是簡單,只要實現FileFilter超類中的兩個方法便可:
public boolean accept(File f); public String getDescription();
第一個方法檢測是否應該接受一個文件,第二個方法返回顯示在文件選擇器對話框中顯示的文件類型的描述信息。
註釋:在java.io包中有一個無關的FileFilter接口,其中只包含一個方法:boolean accept(File f)。File類中的listFiles方法利用它顯示目錄中的文件。咱們不知道Swing的設計者爲何不擴展這個接口,多是由於Java類庫過於複雜,導致Sun程序員也不太熟悉全部的標準類和接口了。 須要解決同時導入javax.io包和javax.swing.filechooser包帶來的名稱衝突問題。最簡 單的方法是導入javax.swing.filechooser.FileFilter, 而不要導入javax.swing.filechooser.*
一旦有了文件過濾器對象,就能夠調用JFileChooser類中的setFileFilter方法,將這個對象安裝到文件選擇器對象中:
chooser.setFileFilter(new FileNameExtensionFilter("Image files", "gif", "jpg"));
能夠爲一個文件選擇器安裝多個過濾器:
chooser.addChoosableFileFilter(filterl); chooser.addChoosableFileFilter(filter2);
用戶能夠從文件對話框底部的組合框中選擇過濾器。在默認狀況下,All files過濾器老是顯示在組合框中。這是一個很好的主意,特別是在使用這個程序的用戶須要選擇一個具備非標準擴展名的文件時。然而,若是你想禁用All files過濾器,須要調用:
chooser.setAcceptAllFileFilterUsed(false);
警告:若是爲加載和保存不一樣類型的文件重用一個文件選擇器,就須要調用:
chooser.resetChoosableFilters();這樣能夠在添加新文件過濾器以前清除舊文件過濾器。
最後,能夠經過爲文件選擇器顯示的每一個文件提供特定的圖標和文件描述來定製文件選擇器。這須要應用一個擴展於javax.swing.filechooser包中的FileView類的對象。這是一個高級技巧。在一般狀況下,不須要提供文件視圖---可插觀感會提供。然而,若是想讓某種特定的文件類型顯示不一樣的圖標,就須要安裝本身的文件視圖。這要擴展FileView並實現下面5個方法:
Icon getIcon(File f); String getName(File f); String getDescription(File f); String getTypeDescription(File f); Boolean isTraversable(File f);
而後,調用setFileView方法將文件視圖安裝到文件選擇器中。
文件選擇器爲每一個但願顯示的文件或目錄調用這些方法。若是方法返回的圖標、名字或描述信息爲null, 那麼文件選擇器將會構造當前觀感的默認文件視圖。這樣處理很好,其緣由是這樣只需處理具備不一樣顯示的文件類型。
文件選擇器調用isTraversable方法來決定是否在用戶點擊一個目錄的時候打開這個目錄。請注意,這個方法返回一個Boolean對象,而不是boolean值。看起來彷佛有點怪,但實際上很方便---若是須要使用默認的視圖,則返回null。文件選擇器將會使用默認的文件視圖。換句話說,這個方法返回的Boolean對象能給出下面三種選擇:真(Boolean.TRUE), 假(Boolean.FALSE)和不關心(null)。
在示例中包含了一個簡單的文件視圖類。當文件匹配文件過濾器時,這個類將會顯示一個特定的圖標。能夠利用這個類爲全部的圖像文件顯示一個調色板圖標。
class FilelconView extends FileView { private FileFilter filter; private Icon icon; public FileIconView(FileFilter aFilter, Icon anlcon) { filter = aFilter; icon = anlcon; } public Icon getIcon(File f) { if (If.isDirectory() && filter.accept(f)) return icon; else return null; } }
能夠調用setFileView方法將這個文件視圖安裝到文件選擇器中:
chooser.setFileView(new FilelconView(filter, new ImageIcon("palette.gif")));
文件選擇器會在經過filter的全部文件旁邊顯示調色板圖標,而且使用默認的文件視圖來顯示全部其餘的文件。很天然,咱們可使用與文件選擇器設定的同樣的過濾器。
提示:能夠在JDK的demo/jfc/FileChooserDemo目錄下找到更實用的ExampleFileView類。它能夠將圖標和描述信息與任意擴展名關聯起來。
最後,能夠經過添加一個附件組件來定製文件對話框。例如,圖12*41在文件列表旁邊顯示了一個預覽附件。這個附件顯示了當前選擇文件的縮略視圖。
附件能夠是任何Swing組件。在這個示例中,擴展JLabel類,並將圖標設置爲所選的圖像文件的壓縮拷貝。
class ImagePreviewer extends JLabel { public ImagePreviewer(FileChooser chooser) { setPreferredSize(new Dimension(100, 100)); setBorder(BorderFactory.createEtchedBorder()); } public void loadImage(File f) { Imagelcon icon = new Imagelcon(f.getPath()); if(icon.getIconWidth() > getWidth()) icon = new ImageIcon(icon.getlmage().getScaledlnstance(getWidth(), -1 , Image.SCALEJEFAULT)); setIcon(icon); repaint(); } }
這裏還有一個挑戰,即須要在用戶選擇不一樣的文件時更新預覽圖像。文件選擇器使用了JavaBeans機制。當它的屬性發生變化時,文件選擇器就會通知相關的監聽器。被選擇文件是一個屬性,能夠經過安裝PropertyChangeListener監聽它。本書將在卷II第8章中討論這個機制。下面這段代碼能夠用來捕捉通知:
chooser.addPropertyChangeListener(event -> { if (event.getPropertyName() == JFileChooser.SELECTED_FILE_CHANCED_PROPERTY) { File newFile = (File) event.getNewValue(); // update the accessory ... } });
在這個示例中,將這段代碼添加到ImagePreviewer構造器中。
程序清單12-21~程序清單12-23對第2章中的ImageViewer程序作了必定的修改。經過自定義的文件視圖和預覽附件文件加強了文件選擇器的功能。
程序清單12-21 fileChooser/lmageViewerFrame.java
package fileChooser; import java.io.*; import javax.swing.*; import javax.swing.filechooser.*; import javax.swing.filechooser.FileFilter; /** * A frame that has a menu for loading an image and a display area for the * loaded image. */ public class ImageViewerFrame extends JFrame { private static final int DEFAULTJUDTH:300; private static final int DEFAULTJEICHT = 400; private JLabel label; private JFileChooser chooser; public ImageViewerFrame() { setSize(DEFAULT_WIDTH, DEFAULTJEIGHT); //set up menu bar JNenuBar menuBar = new DMenuBar(); setJMenuBar(menuBar); JMenu menu = new JMenu("File"); menuBar.add(menu); JMenuItem openltem = new JMenuItem("Open"); menu.add(openltem); openltem.addActionListener(event -> { chooser.setCurrentDirectory(new File(".")); // show file chooser dialog int result = chooser.showOpenDialog(ImageViewerFrame.this); // if image file accepted, set it as icon of the label if (result == JFileChooser.APPR0VE_0PTI0N) { String name = chooser.getSelectedPile().getPath(); label.setIcon(new Imagelcon(name)); pack(); } }); JMenuItem exitltem = new JMenuItem("Exit"); menu.add(exitltem); exitltem.addActionListener(event -> System,exit(0)); // use a label to display the images label = new JLabel(); add(label); // set up file chooser chooser = new JFileChooser(); // accept all image files ending with .jpg, .jpeg, .gif FileFilter filter = new FileNameExtensionFilter( "Image files", "jpg", "jpeg", "gif"); chooser.setFileFilter(filter); chooser.setAccessory(new ImagePreviewer(chooser)); chooser.setFileView(new FileIconView(filter, new Imagelcon("palette.gif"))); } }
程序清單 12-22 fileChooser/lmagePreviewer.java
package fileChooser; import java.awt.*; import java.io.*; import javax.swing.*; /** * A file chooser accessory that previews images. */ public class ImagePreviewer extends JLabel { /** * Constructs an ImagePreviewer. * @param chooser the file chooser whose property changes trigger an image * change in this previewer */ public ImagePreviewer(JFileChooser chooser) { setPreferredSize(new Dimension(100, 100)); setBorder(BorderFactory.createEtchedBorder()); chooser.addPropertyChangeListener(event -> { if (event.getPropertyName() == JFileChooser.SELECTED_FILE_CHANGED_PROPERTY) { // the user has selected a new file File f = (File) event.getNewValue(); if (f == null) { setlcon(null); return; } // read the image into an icon ImageIcon icon = new ImageIcon(f.getPath()); // if the icon is too large to fit, scale it if (icon.getlconWidth() > getWidth()) icon = new Imagelcon(icon.getlmage().getScaledlnstance( getWidth(), -1 , Image.SCALEJEFAULT)); setIcon(icon); } }); } }
程序清單12-23 fileChooser/FilelconView.java
package fileChooser; import java.io.*; import javax.swing.*; import javax.swing.filechooser.*; import javax.swing.filechooser.FileFiIter; /** * A file view that displays an icon for all files that match a file filter. */ public class FilelconView extends FileView { private FileFilter filter; private Icon icon; /** * Constructs a FilelconView. * @param aFilter a file filter all files that this filter accepts will be shown * with the icon. * @param anlcon the icon shown with all accepted files. */ public FilelconView(FileFilter aFilter, Icon anlcon) { filter = aFilter; icon = anlcon; } public Icon getIcon(File f) { if (!f.isDirectory() && filter.accept(f)) return icon; else return null; } }
javax.swing.JFileChooser 1.2
JFileChooser(); //建立一個可用於多框架的文件選擇器對話框。 void setCurrentDirectory(File dir); //設置文件對話框的初始目錄。 void setSelectedFile(File file) void setSelectedFiles(File[] file); //設置文件對話框的默認文件選擇。 void setMultiSelectionEnabled(boolean b); //設置或清除多選模式。 void setFileSelectionMode(int mode); //設置用戶選擇模式,只能夠選擇文件(默認),只能夠選擇目錄,或者文件和目錄都可以選擇。mode參數的取值能夠是JFileChooser.FILES_ONLY、JFileChooser.DIRECTORIES_ONLY和JFileChooser.FILES_AND_DIRECTORIES 之一。 int showOpenDialog(Component parent) int showSaveDialog(Component parent) int showDialog(Component parent, String approveButtonText); //顯 示 按 鈕 標 籤 爲 Open, Save或者approveButtonText字符串的對話框,並返回APPROVE_ OPTION、CANCEL_OPTION (若是用戶選擇取消按鈕或者離開了對話框) 或者ERROR_OPTION (若是發生錯誤)。 File getSelectedFile() File[] getSelectedFiles(); //獲取用戶選擇的一個文件或多個文件(若是用戶沒有選擇文件,返回 null)。 void setmeFilter(FileFilter filter); //設置文件對話框的文件過濾器。全部讓 filteraccqrt 返回 true 的文件都會被顯示,而且 將過濾器添加到可選過濾器列表中。 void addChoosableFileFilter(FileFilter filter); //將文件過濾器添加到可選過濾器列表中。 void setAcceptAllFileFilterUsed(boolean b); //在過濾器組合框中包括或者取消 All files 過濾器。 void resetChoosableFi1eFilters(); //清除可選過濾器列表。除非 All files 過濾器被顯式地清除,不然它仍然會存在。 void setFileView(FileView view); //設置一個文件視圖來提供文件選擇器顯示信息。 void setAccessory(JComponent component); //設置一個附件組件。
javax.swing.filechooser.FileFilter 1.2
boolean accept(File f); //若是文件選擇器能夠顯示這個文件,返回true。 String getDescription(); //返回這個文件過濾器的說明信息,例如,Image files (*.gif, *jpeg)。
javax.swing.filechooser.FileNameExtensionFiler
FileNameExtensionFilter(String description, String ... extensions); //利用給定的描述構造一個文件過濾器。這些描述限定了被接受的全部目錄和文件其名 稱結尾的句點以後所包含的擴展字符串。
javax.swing.filechooser.FileView 1.2
String getName(File f); //返回文件f的文件名,或者null。一般這個方法返回f.getName()。 String getDescription(File f); //返回文件f的可讀性描述,或者null。例如,若是f是HTML文檔,那麼這個方法有可能返回它的標題。 String getTypeDescription(File f); //返回文件f的類型的可讀性描述。例如,若是f是HTML文檔, 那麼這個方法有可能返回Hypertext document字符串。 Icon getIcon(File f); //返回文件f的圖標,或者null。例如,若是f是JPEG文件,那麼這個方法有可能返回簡略的圖標。 Boolean isTraversable(File f); //若是f是用戶能夠打開的目錄,返回Boolean.TRUE若是目錄在概念上是複合文檔, 那麼這個方法有可能返冋false。與全部的FileView方法同樣,這個方法有可能返回null, 用於表示文件選擇器應該使用默認視圖。