Container cp=f.getContentPane();
JFrame 是 Swing 的主要容器之一, 其結構由數個面板 (panel) 組成, 如下圖所示 :
我們所看到的 JFrame 事實上主要由兩個部分組成, 一個是頂端用來放功能列的 menuBar 面板, 而其下方則為放置一般圖形化元件的 ContentPane. 而 glassPane 則用來放彈出式選單 (popup menu).
首先來看 Swing 的按鈕元件 JButton. 建立按鈕可呼叫 JButton 最常用的建構子 JButton(String text), 傳入參數為欲顯示在按鈕上的文字, 再呼叫 ContentPane 的 add() 方法把按鈕加入內容面板即可, 如下列範例 1 所示 :
JButton b1=new JButton("確定");
cp.add(b1);
注意, 以下所有範例中的原始檔均以 utf-8 存檔, 因此編譯時要加入編碼指定參數 :
javac -encoding utf-8 JButton1.java
測試範例 1 : http://tony1966.16mb.com/javatest/JButton1.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton1 {
JFrame f;
public static void main(String argv[]) {
new JButton1();
}
public JButton1() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton1");
f.setBounds(0,0,400,300);
f.setVisible(true);
Container cp=f.getContentPane();
JButton b1=new JButton("確定");
cp.add(b1);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
}
結果此按鈕會占據整個視窗. 原因是 JFrame 預設排版方式為 BorderLayout (邊界排版, 也是 JApplet 的預設排版), 整個視窗被分成東, 西, 南, 北, 中五個方位, 如果 JFrame 的內容面板在加入元件時, 沒有指定擺放位置, 預設就是放在中央位置, 而且就算你用 setSize() 方法設定尺寸也是沒用, 它會自動放大到該方位的最大尺寸. 上面範例 1 因為按鈕 add 時沒有指定方位, 預設會放在中央位置, 且東西南北均無元件, 因此按鈕占用了整個視窗.
邊界排版由 BorderLayout 類別負責, 它定義了五個方位常數作為第二個參數供 add() 方法來指定擺放方位 :
- BorderLayout.CENTER (中)
- BorderLayout.EAST (東)
- BorderLayout.WEST (西)
- BorderLayout.SOUTH (南)
- BorderLayout.NORTH (北)
下列範例 1-1 我們指定按鈕放在東方 :
測試範例 1-1 : http://tony1966.16mb.com/javatest/JButton1_1.zip [看原始碼]
Container cp=f.getContentPane();
JButton b1=new JButton("確定");
cp.add(b1,BorderLayout.EAST);
可見南北無元件時, 東西方向的元件會與視窗同高, 寬度就是按鈕之寬度. 下列範例 1_2 則是把按鈕放在北方位置 :
測試範例 1-2 : http://tony1966.16mb.com/javatest/JButton1_2.zip [看原始碼]
Container cp=f.getContentPane();
JButton b1=new JButton("確定");
cp.add(b1,BorderLayout.NORTH);
可見南北方向的元件會與視窗同寬. 下列範例 2 則是在五個方位都擺放按鈕的情形 :
測試範例 2 : http://tony1966.16mb.com/javatest/JButton2.zip [看原始碼]
Container cp=f.getContentPane();
JButton center=new JButton("中");
JButton east=new JButton("東");
JButton west=new JButton("西");
JButton south=new JButton("南");
JButton north=new JButton("北");
cp.add(center,BorderLayout.CENTER);
cp.add(east,BorderLayout.EAST);
cp.add(west,BorderLayout.WEST);
cp.add(south,BorderLayout.SOUTH);
cp.add(north,BorderLayout.NORTH);
可見當南北有元件時, 東西元件的高度就會被南北的高度吃掉一些. 如果把上例中的 CENTER 元件拿掉, 中央位置就會空在那裏, 如下範例 2-1 所示 :
測試範例 2-1 : http://tony1966.16mb.com/javatest/JButton2_1.zip [看原始碼]
如果拿掉西邊的按鈕, 只放東南北中, 則西邊的空間會被中央佔據, 如下面範例 2-2 所示 :
測試範例 2-2 : http://tony1966.16mb.com/javatest/JButton2_2.zip [看原始碼]
同樣地, 若南方不放元件, 其空間會被東西中佔據, 如下列範例 2-3 所示 :
測試範例 2-3 : http://tony1966.16mb.com/javatest/JButton2_3.zip [看原始碼]
另外我們也可以設定 BorderLayout 各區塊間的間隙, 這必須自行建立一個新的 BorderLayout 物件, 呼叫其可指定間隙之建構子, 然後呼叫內容面板之 setLayout() 方法即可覆蓋原來預設之無間隙邊界排版 :
BorderLayout border=new BorderLayout(3,3);
cp.setLayout(border);
其中第一參數是垂直間隙, 第二參數是水平間隙, 單位是 px. 下面範例 2-4 中我們使用匿名方式來處理 :
測試範例 2-4 : http://tony1966.16mb.com/javatest/JButton2_4.zip [看原始碼]
Container cp=f.getContentPane();
cp.setLayout(new BorderLayout(3,3));
JButton center=new JButton("中");
JButton east=new JButton("東");
JButton west=new JButton("西");
JButton south=new JButton("南");
JButton north=new JButton("北");
cp.add(center,BorderLayout.CENTER);
cp.add(east,BorderLayout.EAST);
cp.add(west,BorderLayout.WEST);
cp.add(south,BorderLayout.SOUTH);
cp.add(north,BorderLayout.NORTH);
邊界排版每一個方位只能放一個元件, 如果再放一個元件, 則新的會蓋掉舊的, 將範例 2-4 改為北方再放一個按鈕 north2, 如下列範例 2-5 所示 :
測試範例 2-5 : http://tony1966.16mb.com/javatest/JButton2_5.zip [看原始碼]
JButton east=new JButton("東");
JButton west=new JButton("西");
JButton south=new JButton("南");
JButton north=new JButton("北");
JButton north2=new JButton("北2");
cp.add(center,BorderLayout.CENTER);
cp.add(east,BorderLayout.EAST);
cp.add(west,BorderLayout.WEST);
cp.add(south,BorderLayout.SOUTH);
cp.add(north,BorderLayout.NORTH);
cp.add(north2,BorderLayout.NORTH);
可見 north 按鈕被 north2 覆蓋掉了. 如果要在北方放三個按鈕該怎麼做呢? 這要用到 Swing 另外一個好用容器 JPanel, 事實上, JFrame 的內容面板預設也是一個 JPanel, 此容器可用來包裹各式元件, 再加入內容面板中, 可以用在複雜的版面設計, 達到彈性排版的目的. JPanel 本身也是有自己的排版管理員的, 預設是 FlowLayout (流動排版).
JPanel 最常用的兩個建構子為 :
JPanel panel=new JPanel(); //預設為 FlowLayout
JPanel panel=new JPanel(new GridLayout()); //指定為表格排版
在 JPanel 內放置元件, 只要呼叫 JPanel 的 add() 方法即可 :
panel.add(new JButton("按鈕"));
我們修改範例 2-4, 在北方位置放三個按鈕, 如下列範例 2-6 所示 :
測試範例 2-6 : http://tony1966.16mb.com/javatest/JButton2_6.zip [看原始碼]
JButton center=new JButton("中");
JButton east=new JButton("東");
JButton west=new JButton("西");
JButton south=new JButton("南");
JButton north1=new JButton("北1");
JButton north2=new JButton("北2");
JButton north3=new JButton("北3");
JPanel panel=new JPanel();
panel.add(north1); //按鈕先放入 JPanel 中
panel.add(north2);
panel.add(north3);
cp.add(center,BorderLayout.CENTER);
cp.add(east,BorderLayout.EAST);
cp.add(west,BorderLayout.WEST);
cp.add(south,BorderLayout.SOUTH);
cp.add(panel,BorderLayout.NORTH); //再把 JPanel 放入北方方位
我們也可以在 ContenPane 呼叫 setLayout() 時傳入 null 來取消預設之 BorderLayout, 改由我們自行排版, 把範例 1 改為如下範例 3 :
測試範例 3 : http://tony1966.16mb.com/javatest/JButton3.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton3 {
JFrame f;
public static void main(String argv[]) {
new JButton3();
}
public JButton3() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton3");
f.setBounds(0,0,400,300);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(null); //取消預設之 BorderLayout
JButton b1=new JButton("確定");
b1.setBounds(20,20,100,40); //自行決定元件位置與大小
cp.add(b1);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
}
可見按鈕會在距離內容面板左上角座標 (20,20) 處顯示, 尺寸為長 100px, 寬 40px. 注意, 取消預設排版後, 元件一定要用 setBounds() 或 setLocation() + setSize() 自行排版, 否則加入內容面板後將看不到該元件 (可將上面之 setBounds 拿掉即知).
JButton 還有一個建構子 JButton(Icon icon) 可以傳入一個實作 Icon 介面之圖像物件, 可用來製作圖像按鈕. 這 Icon 是一個介面, 定義了圖片物件化的方法, Java 中有許多類別實作了此介面, 其中最常用的是 ImageIcon 類別, 可用來將指定檔名之圖檔讀取為圖檔物件, 支援的圖檔類型為 GIF, JPG, PNG 三種. 我們可將其傳入 JButton 建構子, 如下列範例 4 所示 :
ImageIcon icon=new ImageIcon("icon.jpg");
JButton b1=new JButton(icon);
如果圖檔位於其他目錄, 需給予正確路徑, 例如 new ImageIcon("../image/icon.jpg").
測試範例 4 : http://tony1966.16mb.com/javatest/JButton4.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton4 {
JFrame f;
public static void main(String argv[]) {
new JButton4();
}
public JButton4() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton4");
f.setBounds(0,0,400,300);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(null);
ImageIcon icon=new ImageIcon("icon.jpg");
JButton b1=new JButton(icon);
b1.setBounds(20,20,50,50);
cp.add(b1);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
}
也可以呼叫 setIcon() 方法, 如下列範例 4-1 所示 :
測試範例 4-1 : http://tony1966.16mb.com/javatest/JButton4_1.zip [看原始碼]
ImageIcon icon=new ImageIcon("icon.jpg");
JButton b1=new JButton();
b1.setIcon(icon);
b1.setBounds(20,20,50,50);
可見效果與範例 4 是一樣的. 這跟文字按鈕呼叫沒有參數的建構子, 然後再呼叫 setText() 去設定顯示文字是一樣的.
JButton 還有一個建構子 JButton(String text, Icon icon) 可以同時設定文字與圖像. 在下列範例 4-2 中, 我們還要測試按鈕提示文字 (Tool Tips) 功能, 即當滑鼠移到按鈕上時, 會彈出一個浮動小視窗, 顯示文字訊息, 這要用到 JButton 繼承自 JComponet 的方法 setToolTipText(String text) 方法 :
測試範例 4-2 : http://tony1966.16mb.com/javatest/JButton4_2.zip [看原始碼]
Container cp=f.getContentPane();
cp.setLayout(null);
ImageIcon icon=new ImageIcon("icon.jpg");
JButton b1=new JButton("確定",icon);
b1.setToolTipText("文字+圖片按鈕");
b1.setBounds(20,20,150,100);
cp.add(b1);
可見當滑鼠移進此按鈕範圍時, 就會浮現提示文字. Swing 幾乎全部元件都是繼承自 JComponent 類別, 因此都可以呼叫 setToolTipText() 方法來設定提示文字.
接著來看看按鈕事件. 按鈕按下時觸發動作事件 (Action Event), 此事件在 Java 中由 ActionListener 這個介面處理, 因 Action Event 為語意型事件, 只有一個方法 actionPerformed() 需要實作, 因此沒有提供實作此介面之 Adapter 類別, 需自行實作. 如下列範例 5 所示 :
測試範例 5 : http://tony1966.16mb.com/javatest/JButton5.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton5 implements ActionListener{
JFrame f;
public static void main(String argv[]) {
new JButton5();
}
public JButton5() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton5");
f.setSize(400,300);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(null);
JButton b1=new JButton("確定");
b1.setBounds(20,20,100,40);
b1.addActionListener(this);
cp.add(b1);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(f,
"您按了" + e.getActionCommand(),
"訊息",JOptionPane.INFORMATION_MESSAGE);
}
}
此例中我們呼叫 JButton 的 addActionListener() 方法, 將按鈕的動作事件委託給實作 ActionListener 的物件本身 (this) 來監視, 當按鈕被按下時監聽者即呼叫 actionPerformed() 方法來處理 (傳入 ActionEvent 物件), 在此方法中, 我們可以用 e.getActionCommand() 方法來取得按鈕上的文字, 再用JOptionPane 訊息框顯示出來.
當然我們也可以用內部類別來處理, 如範例 5-1 所示 :
測試範例 5-1 : http://tony1966.16mb.com/javatest/JButton5_1.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton5_1 {
JFrame f;
public static void main(String argv[]) {
new JButton5_1();
}
public JButton5_1() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton5_1");
f.setSize(400,300);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(null);
JButton b1=new JButton("確定");
b1.setBounds(20,20,100,40);
b1.addActionListener(new ActionHandler());
cp.add(b1);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
class ActionHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(f,
"您按了" + e.getActionCommand(),
"訊息",JOptionPane.INFORMATION_MESSAGE);
}
}
}
改用內部類別時, 監聽者就不是 JFrame 物件本身 (this) 了, 因為它沒有實作 ActionListener, 而是有實作此介面之內部類別 ActionHandler 之實體. 如果程式中只有一個動作事件要監聽, 就不需要為其指定參考變數, 直接用 new 以匿名物件方式傳入參數即可. 但是若有兩個以上按鈕要監視, 則應指定一個參考, 一併監視與處理為宜, 可以節省記憶體.
我們將範例 5 修改為下面範例 6, 事件處理完全不變, 僅增加取消按鈕 :
測試範例 6 : http://tony1966.16mb.com/javatest/JButton6.zip [看原始碼]
JButton ok=new JButton("確定");
JButton cancel=new JButton("取消");
ok.setBounds(20,20,100,40);
cancel.setBounds(150,20,100,40);
ok.addActionListener(this);
cancel.addActionListener(this);
cp.add(ok);
cp.add(cancel);
可見 e.getActionCommand() 會取得不同按鈕上的文字, 這個方法會傳回一個字串, 如果沒有呼叫 JButton 的 setActionCommand() 方法去特別設定, 則預設就是按鈕的顯示文字. 因此如果程式中有好幾個地方都有確定按鈕, 每一個按鈕就必須個別用 setActionCommand() 去設定不同之動作指令. 在下列範例 6-1 中, 我們放了三個確定按鈕, 但設定不同之動作指令以資辨別事件來源, 其中第三個按鈕利用呼叫 setEnabled(false) 將其禁能 :
測試範例 6-1 : http://tony1966.16mb.com/javatest/JButton6-1.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton6_1 implements ActionListener{
JFrame f;
public static void main(String argv[]) {
new JButton6_1();
}
public JButton6_1() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton6_1");
f.setSize(400,300);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(null);
JButton ok1=new JButton("確定");
JButton ok2=new JButton("確定");
JButton ok3=new JButton("確定");
ok3.setEnabled(false);
ok1.setBounds(20,20,100,40);
ok2.setBounds(150,20,100,40);
ok3.setBounds(280,20,100,40);
ok1.setActionCommand("ok1");
ok2.setActionCommand("ok2");
ok3.setActionCommand("ok3");
ok1.addActionListener(this);
ok2.addActionListener(this);
ok3.addActionListener(this);
cp.add(ok1);
cp.add(ok2);
cp.add(ok3);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(f,
"您按了" + e.getActionCommand(),
"訊息",JOptionPane.INFORMATION_MESSAGE);
}
}
可見經過 setActionCommand() 設定後, 傳回的動作指令就不再是預設的按鈕文字了.
辨別按鈕事件的來源除了上述的動作指令 (Action Command) 外, 還可以呼叫動作事件物件的 getSource() 方法, 此方法會傳回觸發動作事件之物件參考 (即元件之變數名), 可用來比對找出事件來源, 如下列範例 6-2 所示 :
測試範例 6-2 : http://tony1966.16mb.com/javatest/JButton6-2.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton6_2 implements ActionListener{
JFrame f;
JButton ok,cancel;
public static void main(String argv[]) {
new JButton6_2();
}
public JButton6_2() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton6_2");
f.setSize(400,300);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(null);
ok=new JButton("確定");
cancel=new JButton("取消");
ok.setBounds(20,20,100,40);
cancel.setBounds(150,20,100,40);
ok.addActionListener(this);
cancel.addActionListener(this);
cp.add(ok);
cp.add(cancel);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
public void actionPerformed(ActionEvent e) {
String btn="";
if (e.getSource()==ok) {btn="確定";}
else if (e.getSource()==cancel) {btn="取消";}
JOptionPane.showMessageDialog(f,
"您按了" + btn,
"訊息",JOptionPane.INFORMATION_MESSAGE);
}
}
結果與範例 6 是一樣的, 但這裡要特別注意 JButton 的宣告位置已經改為類別成員了, 而非範例 6 建構子中的區域變數, 這樣事件處理方法 actionPerformed() 才能存取到這兩個按鈕變數 (參考), 否則會因不在 scope 內而在編譯時出現 "cannot find symbol" 的錯誤. 事實上, Swing 程式中每一個跟事件處理有關的 GUI 元件都應該宣告為類別成員, 事件處理會比較方便. 也最好不要在 main() 方法中建立元件實體 (應該在建構子或方法中), 因為 main() 方法裡只能存取靜態屬性與靜態方法, 每一個成員元件都要宣告為 static 較麻煩.
下面範例 6-3 我們再加入一個關閉按鈕來結束視窗, 這可在呼叫處理中執行 System.exit(0) 達成, 同時為按鈕加上快捷鍵 (Mnemonic), 亦即按下 Alt + 指定字母相當於用滑鼠按下該按鈕, 這要呼叫 JButton 的 setMnemic() 方法, 並傳入一個字元 :
測試範例 6-3 : http://tony1966.16mb.com/javatest/JButton6-3.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton6_3 implements ActionListener{
JFrame f;
JButton ok,cancel,quit;
public static void main(String argv[]) {
new JButton6_3();
}
public JButton6_3() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton6_3");
f.setSize(400,300);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(null);
ok=new JButton("確定(O)");
cancel=new JButton("取消(C)");
quit=new JButton("關閉(Q)");
ok.setBounds(20,20,100,40);
cancel.setBounds(150,20,100,40);
quit.setBounds(280,20,100,40);
ok.setMnemonic('O'); //注意是字元, 不是字串
cancel.setMnemonic('C');
quit.setMnemonic('Q');
ok.addActionListener(this);
cancel.addActionListener(this);
quit.addActionListener(this);
cp.add(ok);
cp.add(cancel);
cp.add(quit);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
public void actionPerformed(ActionEvent e) {
String btn="";
if (e.getSource()==quit) {System.exit(0);}
else if (e.getSource()==ok) {btn="確定";}
else if (e.getSource()==cancel) {btn="取消";}
JOptionPane.showMessageDialog(f,
"您按了" + btn,
"訊息",JOptionPane.INFORMATION_MESSAGE);
}
}
用滑鼠按下關閉鈕, 程式會立即結束, 或者同時按 ALT+Q 亦可, 這就是快捷鍵功能, 當沒有滑鼠時也能操控程式.注意, setMnemonic() 方法的參數是字元 (char), 要用單引號, 不可用雙引號 (字串), 否則編譯時會出現 no suitable method for setMnemonic, 因為 JButton 沒有定義參數為字串的方法.
以下接著測試第二種排版方式 : FlowLayout, 這也是 Swing 另一個好用的容器 JPanel 的預設版面. 此排版方式會依元件加入的先後順序, 依序先由左到右, 滿了再由上而下排列, 且會在視窗縮放時自動重新排列. 我們只要建立一個 FlowLayout 物件傳給內容版面的 setLayout() 當參數即可. 如下範例 7 所示 :
測試範例 7 : http://tony1966.16mb.com/javatest/JButton7.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton7 {
JFrame f;
JButton b1,b2,b3,b4,b5;
public static void main(String argv[]) {
new JButton7();
}
public JButton7() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton7");
f.setSize(300,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(new FlowLayout());
b1=new JButton("按鈕 1");
b2=new JButton("按鈕 2");
b3=new JButton("按鈕 3");
b4=new JButton("按鈕 4");
b5=new JButton("按鈕 5");
cp.add(b1);
cp.add(b2);
cp.add(b3);
cp.add(b4);
cp.add(b5);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
}
此例中我們呼叫 FlowLayout 之無參數建構子, 預設是元件置中對齊, 水平與垂直間隙 5px. 注意, FlowLayout 時元件會以其原有的尺寸出現, 不會像 BorderLayout 那樣強制將元件放大到佔據其所分配到的區域, 但是 setSize(), setLocation(), 以及 setBounds() 仍然是無效的 (只要指定一個 Layout, 這三個方法就沒作用). FlowLayout 還有其他兩個建構子 :
- new FlowLayout(int align) :
可用參數有 3 個 :
FlowLayoy.CENTER (預設), FlowLayoy.LEFT, FlowLayoy.RIGHT - new FlowLayout(int align, int hgap, int vgap) :
hgap 為水平間隙, vgap 為垂直間隙, 單位 px.
下列範例 7-1 為向左對齊 :
測試範例 7-1 : http://tony1966.16mb.com/javatest/JButton7_1.zip [看原始碼]
cp.setLayout(new FlowLayout(FlowLayout.LEFT));
下列範例 7-2 則為向右對齊, 並設定水平間隙為 7px, 垂直間隙為 10px :
測試範例 7-2 : http://tony1966.16mb.com/javatest/JButton7_2.zip [看原始碼]
cp.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 15));
可見元件都向右對齊, 水平與垂直間隙都增大了.
第三種排版方式為表格排版 GridLayout, 整個版面縱橫分割成如同 Excel 試算表那樣的表格, 當用 add() 將元件加入版面時, 會跟 FlowLayout 一樣依序從左至右, 滿了從上而下擺放元件, 網格座標的原點 (0,0) 在容器左上角, 向右為 x 軸增大, 向下為 y 軸增大.
GridLayout 最常用的建構子是直接指定表格版面之列數與行數 (直的為行或欄, 橫的為列) :
GridLayout(int rows, int cols)
但通常為了控制行數不變, 會把列數設為 0, 如下範例 8 所示 :
測試範例 8 : http://tony1966.16mb.com/javatest/JButton8.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton8 {
JFrame f;
JButton b1,b2,b3,b4,b5;
public static void main(String argv[]) {
new JButton8();
}
public JButton8() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton8");
f.setSize(300,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(new GridLayout(0,3));
b1=new JButton("按鈕 1");
b2=new JButton("按鈕 2");
b3=new JButton("按鈕 3");
b4=new JButton("按鈕 4");
b5=new JButton("按鈕 5");
cp.add(b1);
cp.add(b2);
cp.add(b3);
cp.add(b4);
cp.add(b5);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
}
此例中, 我們呼叫 GridLayout 的建構子 GridLayout(int row, int column) 設定了 0 列 3 行的表格版面, 指定列數為 0, 表示列數不限制, (0, 3) 表示固定行數為 3, 當元素超過一列時, GridLayout 會自動再增加一列. 當然也可以設成 (2, 3) 即二列三行, 但這是在知道元件數目時可以這麼做, 如果元件數目超過 6 個, 它會自動增加行數, 如下列範例 8-1 所示 :
測試範例 8-1 : http://tony1966.16mb.com/javatest/JButton8_1.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton8_1 {
JFrame f;
JButton b1,b2,b3,b4,b5,b6,b7;
public static void main(String argv[]) {
new JButton8_1();
}
public JButton8_1() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton8_1");
f.setSize(300,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(new GridLayout(2,3));
b1=new JButton("按鈕 1");
b2=new JButton("按鈕 2");
b3=new JButton("按鈕 3");
b4=new JButton("按鈕 4");
b5=new JButton("按鈕 5");
b6=new JButton("按鈕 6");
b7=new JButton("按鈕 7");
cp.add(b1);
cp.add(b2);
cp.add(b3);
cp.add(b4);
cp.add(b5);
cp.add(b6);
cp.add(b7);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
}
可見我們雖然指定 2 列 3 行, 但加入 7 個元件進去, 這時它會牽就列數為 2, 自動增加一行, 變成 2 列 4 行. 如果加入內容版面的元件數目不足填滿第一列, GridLayout 會自動刪減行數, 如下列範例 8-2 所示 :
測試範例 8-2 : http://tony1966.16mb.com/javatest/JButton8_2.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton8_2 {
JFrame f;
JButton b1,b2;
public static void main(String argv[]) {
new JButton8_2();
}
public JButton8_2() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton8_2");
f.setSize(300,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(new GridLayout(2,3)); //設定 2X3 表格
b1=new JButton("按鈕 1");
b2=new JButton("按鈕 2");
cp.add(b1);
cp.add(b2); //實際只放兩個元件
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
}
可見雖指定表格為 2 列 3 行, 但因只加入兩個元件, 因此內容版面的 GridLayout 管理員會把行數自動縮減為 2 列 1 行, 顯示 GridLayout 以列為尊的特性. 如果要去除列優先特性, 把欄數 (行數) 固定住, 就要像上面範例 8 那樣, 把列數指定為 0 即可. 我們將範例 8-2 的表格改為 (0,3), 如下列範例 8-3 所示 :
測試範例 8-3 : http://tony1966.16mb.com/javatest/JButton8_3.zip [看原始碼]
cp.setLayout(new GridLayout(0,3));
可見只要把列數指定為 0, 行數就會固定住, 即使元件數不滿一列, 也會空在那邊, 不會自動縮減行數了.
表格排版還有一個建構子可以讓我們指定元件間之水平與垂直間隙 :
GridLayout(int rows, int cols, int hgap, int vgap)
我們修改把範例 8 的建構子部分, 加入間隙參數, 如下範例 8-4 所示 :
測試範例 8-4 : http://tony1966.16mb.com/javatest/JButton8_4.zip [看原始碼]
cp.setLayout(new GridLayout(0,3, 7, 12)); //水平間隙 7px, 垂直 12px
可見只有元件之間有間隙, 與邊界之間則無. 以上便是表格版面的測試.
第三種版面管理員是 Swing 中最有彈性的 GridBagLayout, 但也是最複雜的一個, 因為此種版面要用到 GridBagLayout 與 GridBagConstraints 兩種類別, 而其複雜主要是因為負責設定元件位置限制的 GridBagConstraints 類別之屬性眾多之故. GridBagLayout 是 GridLayout 的延伸, 它不要求元件的大小一致, 而且一個元件可以佔用一個以上儲存格, 因此具有合併儲存格效果, 當容器 (例如 JFrame) 縮放時, 會依元件的權重決定元件縮放幅度, 權重設為 0 者就不會隨視窗縮放而改變.
在 GridBagLayout 中, 元件在表格中的顯示方式 (例如是否要填滿, 元件間之間隙大小等等) 需使用 GridBagConstraints 類別之實體來設定. 建立一個 GridBagConstraints 物件的最簡單方法是呼叫其無參數之建構子, 這樣其各屬性值會套用預設值 :
GridBagConstraints gbc=new GridBagConstraints();
然後再視需要去修改其屬性值. 下列為此類別之屬性, 用來設定元件在版面中的顯示位置 :
- gridx, gridy :
元件的左上角在表格中的座標位置, 預設值均為 0. - gridwidth, gridheight :
元件所佔的網格寬度 (gridwidth) 與 高度 (gridheight), 即跨幾個格子, 預設均為 1. 除了指定明確數值外, 還可以指定下列兩個常數 :
GridBagConstraints.RELATIVE : 此元件將填滿除最後一列或行以外的全部行格或列格.GridBagConstraints.REMAINDER : 此元件將填滿剩下的列格或行格. - weightx, weighty :
分配額外空間時 (例如視窗縮放), 元件在水平 (weightx) 與垂直 (weighty) 方向尺寸變化所佔之權重比例, 預設是 0 (不參與分配, 固定原尺寸不變). 例如第一列元件的 weighty 為 1, 第二列為 2, 則視窗放大時第二列所獲得之額外空間是第一列之兩倍.
例如 : gbc.weightx=1; gbc.weighty=2; - fill : 當元件尺寸小於網格尺寸時, 設定元件的填滿方式, 有下列四種 :
GridBagConstraints.BOTH : 垂直水平皆填滿
GridBagConstraints.HORIZONTAL : 水平填滿
GridBagConstraints.VERTICAL : 垂直填滿
GridBagConstraints.NONE : 垂直水平皆不填滿 (預設)
例如 : gbc.GridBagConstraints.BOTH;
注意, fill 若配合大於 0 之權重, 除了會填滿網格, 還會將整個表格撐大至與視窗同寬或同高. - anchor :
當元件尺寸小於網格尺寸, 而且 fill 屬性設為 NONE (不填滿) 時, 可用 anchor 屬性設定元件在網格中的位置, 有下列 9 個位置 :
GridBagConstraints.CENTER : 中間對齊 (預設)
GridBagConstraints.EAST : 向右對齊, 垂直置中
GridBagConstraints.WEST : 向左對齊, 垂直置中
GridBagConstraints.SOUTH : 向下對齊, 水平置中
GridBagConstraints.NORTH : 向上對齊, 水平置中
GridBagConstraints.NORTHEAST : 向右上對齊
GridBagConstraints.SOUTHEAST : 向右下對齊
GridBagConstraints.SOUTHWEST : 向左下對齊
GridBagConstraints.NORTHWEST : 向左上對齊
例如 : gbc=GridBagConstraints.EAST; - insets :
設定元件與網格邊界之距離, 使用 Insets(int top, int left, int bottom, int right) 類別,
例如 : gbc.insets=new Insets(3,3,3,3);
當設定好 GridBagConstraints 屬性後, 將元件加入內容面板時, 要把 GridBagConstraints 物件傳入 add() 方法中作為第二個參數 :
cp.add(button, gbc);
參考 : 程式語言教學誌
如下列範例 9 所示 :
測試範例 9 : http://tony1966.16mb.com/javatest/JButton9.zip [看原始碼]
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton9 {
JFrame f;
JButton b1,b2,b3;
public static void main(String argv[]) {
new JButton9();
}
public JButton9() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton9");
f.setSize(300,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
Container cp=f.getContentPane();
cp.setLayout(new GridBagLayout());
GridBagConstraints gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.BOTH;
gbc.insets=new Insets(3,3,3,3);
gbc.gridx=0;
gbc.gridy=0;
gbc.gridwidth=3;
gbc.gridheight=3;
gbc.weightx=1;
gbc.weighty=1;
b1=new JButton("按鈕 1");
cp.add(b1,gbc);
gbc.gridx=3;
gbc.gridy=0;
gbc.gridwidth=1;
gbc.gridheight=1;
gbc.weightx=0;
gbc.weighty=0;
b2=new JButton("按鈕 2");
cp.add(b2,gbc);
gbc.gridx=3;
gbc.gridy=1;
gbc.gridwidth=1;
gbc.gridheight=2;
b3=new JButton("按鈕 3");
cp.add(b3,gbc);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
}
此例中按鈕 1 設定行列寬度各為 3, 因此合併儲存格水平垂直各三格. 按鈕 2 座標為 (3,0) 在右上角, 且不參與分配額外空間, 高度最小. 按紐 3 也是合併儲存格 (垂直 2 格).
當縮放視窗時可以發現, 由於按鈕 2 的水平垂直縮放權重均為 0, 故視窗縮放時不參與分配額外空間, 其尺寸維持不變; 按鈕 3 之權重雖然也是 0 (因為沒有重新設定就維持上一次之值), 但因為高度跨 2 格, 因此其高度會隨按鈕 1 縮放而參與分配, 寬度則跟按鈕 2 一樣不變.
除了將 GridBagConstraints 物件傳入內容面板的第二參數來設定該元件的排版位置外, 也可以傳入 GridBagLayout 物件的 setConstraints() 方法的第二參數, 但這樣就不能用匿名的 GridBagLayout 物件了 :
GridBagLayout gbl=new GridBagLayout(); //指定排版物件名稱
cp.setLayout(gbl);
gbl.setConstraints(b1, gbc); // gbc 改傳入 setConstraints()
cp.add(b1); //內容面板不用傳入 gbc
修改範例 9 為範例 9_1 如下 :
測試範例 9_1 : http://tony1966.16mb.com/javatest/JButton9_1.zip [看原始碼]
Container cp=f.getContentPane();
GridBagLayout gbl=new GridBagLayout();
cp.setLayout(gbl);
GridBagConstraints gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.BOTH;
gbc.insets=new Insets(3,3,3,3);
gbc.gridx=0;
gbc.gridy=0;
gbc.gridwidth=3;
gbc.gridheight=3;
gbc.weightx=1;
gbc.weighty=1;
b1=new JButton("按鈕 1");
gbl.setConstraints(b1, gbc); //第二參數傳入元件位置限制
cp.add(b1);
效果與範例 9 是一樣的, 但是元件必須傳給兩個物件的方法, 且不能用匿名方式建立版面管理原物件, 感覺比較麻煩. GridBagLayout 的煩瑣處在於其屬性設定, 我們可以將範例 9 中的位置與大小的設定寫成一個方法 addComponent() 來簡化, 如下列範例 9-2 所示 :
測試範例 9_2 : http://tony1966.16mb.com/javatest/JButton9_2.zip [看原始碼]
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JButton9_2 {
JFrame f;
JButton b1,b2,b3;
Container cp; //改為類別成員
GridBagConstraints gbc; //改為類別成員
public static void main(String argv[]) {
new JButton9_2();
}
public JButton9_2() {
JFrame.setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
f=new JFrame("JButton9_2");
f.setSize(300,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
cp=f.getContentPane();
GridBagLayout gbl=new GridBagLayout();
cp.setLayout(gbl);
gbc=new GridBagConstraints();
gbc.fill=GridBagConstraints.BOTH;
gbc.insets=new Insets(3,3,3,3);
gbc.weightx=1;
gbc.weighty=1;
b1=new JButton("按鈕 1");
addComponent(b1,0,0,3,3);
gbc.weightx=0;
gbc.weighty=0;
b2=new JButton("按鈕 2");
addComponent(b2,3,0,1,1);
b3=new JButton("按鈕 3");
addComponent(b3,3,1,1,2);
f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
int result=JOptionPane.showConfirmDialog(f,
"確定要結束程式嗎?",
"確認訊息",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (result==JOptionPane.YES_OPTION) {System.exit(0);}
}
});
}
public void addComponent(Component c, int row, int column, int width, int height) {
gbc.gridx=row;
gbc.gridy=column;
gbc.gridwidth=width;
gbc.gridheight=height;
cp.add(c,gbc);
}
}
在 addComponent() 方法中, 傳入之第一參數為要放入內容面板之元件, 其餘參數為此元件在網格中的座標與大小. 因為要在此方法中存取內容面板 cp 與 GridBagConstraints 物件, 因此必須將此兩物件特別宣告為類別成員. 使用此方法可以簡化繁瑣的大小尺寸設定, 大幅減少程式碼.
如果將上例中的權重全部設為 0 (預設值), 則即使 fill 屬性為 BOTH, 也只是填滿表格, 不會自動將表格撐到與視窗同高或同寬, 如下列範例 9-3 所示 :
測試範例 9-3 : http://tony1966.16mb.com/javatest/JButton9_3.zip [看原始碼]
gbc.fill=GridBagConstraints.BOTH;
gbc.insets=new Insets(3,3,3,3);
gbc.weightx=0;
gbc.weighty=0;
b1=new JButton("按鈕 1");
addComponent(b1,0,0,3,3);
gbc.weightx=0;
gbc.weighty=0;
b2=new JButton("按鈕 2");
addComponent(b2,3,0,1,1);
b3=new JButton("按鈕 3");
addComponent(b3,3,1,1,2);
如果把按鈕 1 的 weighty 設為 1 (不等於 0 即可), 則按鈕 1 不僅會填滿表格, 其高度還會擴展到視窗高度, 如下列範例 9-4 :
測試範例 9-4 : http://tony1966.16mb.com/javatest/JButton9_4.zip [看原始碼]
gbc.fill=GridBagConstraints.BOTH;
gbc.insets=new Insets(3,3,3,3);
gbc.weightx=0;
gbc.weighty=1;
b1=new JButton("按鈕 1");
addComponent(b1,0,0,3,3);
gbc.weightx=0;
gbc.weighty=0;
b2=new JButton("按鈕 2");
addComponent(b2,3,0,1,1);
b3=new JButton("按鈕 3");
addComponent(b3,3,1,1,2);
同樣地, 若改為只將按鈕 1 的 weightx 設為 1, 則配合 fill 設為 BOTH, 則整個表格水平寬度會被擴展到與視窗同寬, 如下範例 9-5 所示 :
測試範例 9-5 : http://tony1966.16mb.com/javatest/JButton9_5.zip [看原始碼]
gbc.fill=GridBagConstraints.BOTH;
gbc.insets=new Insets(3,3,3,3);
gbc.weightx=1;
gbc.weighty=0;
b1=new JButton("按鈕 1");
addComponent(b1,0,0,3,3);
gbc.weightx=0;
gbc.weighty=0;
b2=new JButton("按鈕 2");
addComponent(b2,3,0,1,1);
b3=new JButton("按鈕 3");
addComponent(b3,3,1,1,2);
讚! 感謝您的經驗分享!
回覆刪除謝謝你的教學! 不然翻書翻到霧煞煞
回覆刪除