2014年2月18日 星期二

Java Swing 測試 : JButton 與排版

測試完 JFrame 後, 接下來要在 JFrame 中置放 UI 元件. 這裡要測試最常用的 JButton 以及排版 (Layout). 與 AWT 不同的是, 在 JFrame 裡面放置元件不是直接 add 在 JFrame 物件上, 而是放在其 ContentPane 容器裡面, 此容器可以透過呼叫 JFrame 物件的 getContentPane() 方法取得, 它會傳回一個 Container 物件 (預設為 JPanel).

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() 方法來指定擺放方位 :
  1. BorderLayout.CENTER (中)
  2. BorderLayout.EAST (東)
  3. BorderLayout.WEST (西)
  4. BorderLayout.SOUTH (南)
  5. BorderLayout.NORTH (北)
BorderLayout 的特性是, 南北方向 (上下) 的元件會與視窗同寬 (高度即元件之高度), 若南北無元件, 東西方向 (左右) 會與視窗同高 (寬度即元件之寬度); 剩下的空間則留給中央位置, 如果東西南北任一區域沒有元件, 就會被其他區域占用, 但中央位置沒擺元件時卻是留白. 

下列範例 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 center=new JButton("中");
    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"). 

測試範例 4http://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-1http://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-2http://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-3http://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 所示 :

測試範例 7http://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 還有其他兩個建構子 :

  1. new FlowLayout(int align) :
    可用參數有 3 個 :
    FlowLayoy.CENTER (預設), FlowLayoy.LEFT, FlowLayoy.RIGHT 
  2. new FlowLayout(int align, int hgap, int vgap) :
    hgap 為水平間隙, vgap 為垂直間隙, 單位 px.

下列範例 7-1 為向左對齊 :

測試範例 7-1http://tony1966.16mb.com/javatest/JButton7_1.zip [看原始碼]

cp.setLayout(new FlowLayout(FlowLayout.LEFT));


下列範例 7-2 則為向右對齊, 並設定水平間隙為 7px, 垂直間隙為 10px :

測試範例 7-2http://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 所示 :

測試範例 8http://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-1http://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();

然後再視需要去修改其屬性值. 下列為此類別之屬性, 用來設定元件在版面中的顯示位置 :
  1. gridx, gridy :
    元件的左上角在表格中的座標位置, 預設值均為 0.
  2. gridwidth, gridheight :
    元件所佔的網格寬度 (gridwidth) 與 高度 (gridheight), 即跨幾個格子, 預設均為 1. 除了指定明確數值外, 還可以指定下列兩個常數 :
    GridBagConstraints.RELATIVE : 此元件將填滿除最後一列或行以外的全部行格或列格.
    GridBagConstraints.REMAINDER : 此元件將填滿剩下的列格或行格.
  3. weightx, weighty :
    分配額外空間時 (例如視窗縮放), 元件在
    水平 (weightx) 與垂直 (weighty) 方向尺寸變化所佔之權重比例, 預設是 0 (不參與分配, 固定原尺寸不變). 例如第一列元件的 weighty 為 1, 第二列為 2, 則視窗放大時第二列所獲得之額外空間是第一列之兩倍.
    例如 : gbc.weightx=1; gbc.weighty=2;
  4. fill : 當元件尺寸小於網格尺寸時, 設定元件的填滿方式, 有下列四種 :
    GridBagConstraints.BOTH : 垂直水平皆填滿
    GridBagConstraints.HORIZONTAL : 水平填滿
    GridBagConstraints.VERTICAL : 垂直填滿
    GridBagConstraints.NONE : 垂直水平皆不填滿 (預設)
    例如 : gbc.GridBagConstraints.BOTH;
    注意, fill 若配合大於 0 之權重, 除了會填滿網格, 還會將整個表格撐大至與視窗同寬或同高.
  5. 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;
  6. 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.*;
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_1http://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_2http://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);



2 則留言 :

匿名 提到...

讚! 感謝您的經驗分享!

匿名 提到...

謝謝你的教學! 不然翻書翻到霧煞煞