2014年2月28日 星期五

樹苺派的 VNC 遠端連線

最近忙著復習測試 Java Swing, 已一週未碰樹苺派板子了, 故索性關機休息, 反正已經兩天熱機測試穩得很 (其實美光晶片就很穩, 除非想超頻, 否則不須要改換三星晶片, 差價約 200 元). 今日在硬派製作看到這一篇 :

# 硬 Pi 製作 - 基礎篇 - VNC 連線
# 遠端遙控軟體 - VNC / UltraVNC 心得

哈哈, 不錯不錯, 這正是我要的. 我按圖索驥, 照著這位大哥的指引操作一番, 哈哈哈, 成功地從我的 Windows 連線樹苺派, 進入 X 視窗了, 以下是操作實錄 :

首先在 Windows 上用 Putty 連線樹苺派, 登入後先鍵入下列指令下載安裝 tightvncserver 套件, 注意中間要輸入 Y 以便繼續安裝 :

pi@raspberrypi ~ $ date
Fri Feb 28 14:04:47 CST 2014
pi@raspberrypi ~ $ sudo apt-get install tightvncserver
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
  xfonts-base
Suggested packages:
  tightvnc-java
The following NEW packages will be installed:
  tightvncserver xfonts-base
0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded. (安裝了兩個額外套件)
Need to get 6,967 kB of archives.
After this operation, 9,988 kB of additional disk space will be used. (需要約 10 M 空間)
Do you want to continue [Y/n]? Y  (按 Y 繼續安裝)
Get:1 http://mirrordirector.raspbian.org/raspbian/ wheezy/main tightvncserver armhf 1.3.9-6.4 [786 kB]
Get:2 http://mirrordirector.raspbian.org/raspbian/ wheezy/main xfonts-base all 1:1.0.3 [6,181 kB]
Fetched 6,967 kB in 7s (913 kB/s)
Selecting previously unselected package tightvncserver.
(Reading database ... 65274 files and directories currently installed.)
Unpacking tightvncserver (from .../tightvncserver_1.3.9-6.4_armhf.deb) ...
Selecting previously unselected package xfonts-base.
Unpacking xfonts-base (from .../xfonts-base_1%3a1.0.3_all.deb) ...
Processing triggers for man-db ...
Processing triggers for fontconfig ...
Setting up tightvncserver (1.3.9-6.4) ...
update-alternatives: using /usr/bin/tightvncserver to provide /usr/bin/vncserver (vncserver) in auto mode
update-alternatives: using /usr/bin/Xtightvnc to provide /usr/bin/Xvnc (Xvnc) in auto mode
update-alternatives: using /usr/bin/tightvncpasswd to provide /usr/bin/vncpasswd (vncpasswd) in auto mode
Setting up xfonts-base (1:1.0.3) ...
pi@raspberrypi ~ $   (出現 SHELL 提示號就表示安裝完畢了)

VNC 套件安裝完畢後, 還要做一些設定, 按照該文步驟,須利用 nano 編輯器複製一份 script 檔叫做 tightvncserver 放在 /etc/init.d/ 下面, 改變此程式為 755 可執行權限後, 加入開機程序中, 這樣只要樹苺派一開機就會啟動 VNC 伺服端了 :

pi@raspberrypi ~ $ sudo nano /etc/init.d/tightvncserver  (編輯 SCRIPT)
pi@raspberrypi ~ $ sudo chmod 755 /etc/init.d/tightvncserver  (更改權限)
pi@raspberrypi ~ $ sudo update-rc.d tightvncserver defaults  (加入開機程序)
update-rc.d: using dependency based boot sequencing
pi@raspberrypi ~ $ tightvncserver  (執行伺服端 SCRIPT)

這時它會詢問密碼, 預設為 12345678 :

You will require a password to access your desktops.

Password: (這裡輸入 12345678 後按 ENTER)
Verify:  (再輸入 12345678 一次)
Would you like to enter a view-only password (y/n)? n  (不是只有看而已, 故輸入 n)

New 'X' desktop is raspberrypi:1

Creating default startup script /home/pi/.vnc/xstartup
Starting applications specified in /home/pi/.vnc/xstartup
Log file is /home/pi/.vnc/raspberrypi:1.log

這樣樹苺派裡的伺服端就 OK 了, 接著要去 TIGHTVNC 網站下載客戶端軟體, 有 Windows 32/64 安裝版的, 也有 Java 免安裝版的 (須安裝 JRE), 我先用 Java 的 (是一個 zip 檔) :




解開 zip 後, 點擊執行其中的 tightvnc-jviewer.jar 這個 Java 壓縮檔, 若已安裝 JRE 就會出現一個用 Swing 寫的登入視窗 :



先在 PuTTY 用 ifconfig 指令查出樹苺派的 IP 為 192.168.2.107, 埠號為 5901, 按 Connect 即連線樹苺派的 VNC 伺服器, 會彈出視窗詢問密碼, 這不是樹苺派本身的密碼, 而是 VNC 的預設密碼 12345678,



按 Login 即出現樹苺派的 X 視窗了, 打開 LXterminal 執行預先用 nano 編輯的 Java Swing 程式 JLabel1.java 如下 :



真不錯, Swing 預設的 Metal 主題到哪裡都長的一模一樣. 要關掉 VNC 連線只要按上方最右邊的叉叉就可以了, 注意, 如果按了那個全螢幕鈕, 要再回到 Windows 可用 Ctrl+Alt+Del :


所以只要一塊樹苺派板子加上網路線就可以了, 根本不須要 HDMI 線.

我在學習上常常因為一時興起就來個 180 度轉向, 像 jQuery 學個七八成功力突然想寫 App, 馬上切換到 HTML5; 剛測試完 GeoLocation, 又因為代班時無聊, 在電腦中找到以前放在硬碟中的 ExtJS 電子書, 看著看著就著迷起來, 又一頭栽入 ExtJS 世界中.

前陣子買了樹苺派, 其 Rasbian Linux 作業系統已經內建 Python 與 Java 開發環境, 呵呵, 為了在 Linux 下執行 Java, 這又搬出陳年老梗的 Java 來玩, 曾經也覺得自己實在太喜新厭舊了 (但常是冷飯熱炒), 功夫沒練全又轉向, 學得太雜, 又忘得太快, 挺煩惱的. 不過, 想到笑傲江湖中風清揚說的, "大丈夫行事愛怎樣便怎樣, 行雲流水, 任意所至, 甚麼武林規矩, 門派教條, 全都是放他媽的狗臭屁!", 沒錯, 生活中太多不想做但又得做之事, 綁手綁腳, 殊屬無奈; 但在學習功夫上, 容許自己任性一下, 想幹啥就幹啥, 沒那麼多條條框框, 豈不快活!

天龍八部劇集



# 天龍八部(2013) 劇集列表
# 天龍八部(2003) 劇集列表

2014年2月24日 星期一

Java Swing 測試 : JLabel 與文字欄位

Swing 中跟文字相關的圖形用戶界面元件有 JLabel,JTextField ,JPassword,JTextArea ,以及 JEditorPane。首先測試 JLabel,此元件是一個文字或圖像標籤(預設向左對齊),用來提供指示或說明,使用者無法編輯。JLabel 的最常用建構子如下:

JLabel label=new JLabel("標籤") ;

或者先呼叫無參數之建構子,再呼叫 setText(String text) 方法設定顯示文字:

JLabel label=new JLabel();
label.setText("標籤");

也可以傳入水平對齊方式常數作為第二參數:

JLabel label=new JLabel("標籤", SwingConstants.RIGHT);

水平對齊方式常數有三個:

SwingConstants.LEFT(預設= 2), SwingConstants.CENTER(= 0), SwingConstants.RIGHT(= 4)

如下範例 1 所示:
 
測試範例1 :  http://mybidrobot.allalla.com/javatest/JLabel1.zip  [ 看原始碼]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class JLabel1 {
  JFrame f;
  public static void main(String argv[]) {
    new JLabel1();
    }
  public JLabel1() {
    JFrame.setDefaultLookAndFeelDecorated(true);
    JDialog.setDefaultLookAndFeelDecorated(true);
    f=new JFrame("JLabel1");
    f.setSize(300,200);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
    Container cp=f.getContentPane();
    cp.setLayout(null);
    JLabel label1=new JLabel("標籤 1");
    label1.setBounds(20,20,100,40);
    cp.add(label1);
    JLabel label2=new JLabel();
    label2.setText("標籤 2");
    label2.setBounds(20,50,100,40);
    cp.add(label2);
    JLabel label3=new JLabel("標籤 3",SwingConstants.RIGHT);
    label3.setBounds(20,80,100,40);
    cp.add(label3);
    JLabel label4=new JLabel("標籤 4",SwingConstants.CENTER);
    label4.setBounds(20,110,100,40);
    cp.add(label4);
    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);}
        }  
      });
    }
  }

除了文字之外,JLabel的也可以使用圖片,這要在呼叫建構子時,傳入 ImageIcon 物件:

ImageIcon icon=new ImageIcon("icon.jpg");
JLabel label=new JLabel(icon);

也可以先呼叫無參數建構子,再呼叫 SetIcon() 方法傳入 IconImage 物件:

JLabel label =new JLabel();
label.setIcon(new IconImage("icon.jpg"));

或者呼叫有水平對齊參數之建構子:

JLabel label =new JLabel(icon, SwingConstants.RIGHT);

如下面範例 2 所示:

測試範例 2 :  http://mybidrobot.allalla.com/javatest/JLabel2.zip  [ 看原始碼]

    ImageIcon的圖標=新的ImageIcon(“icon.jpg”) ;
    JLabel的標籤1 =新JLabel的(圖標) ; / /預設圖片置中對齊
    label1.setBounds(20,20,100,50);
    label1.setToolTipText(“標籤1”);
    cp.add項(Label);
    JLabel的標記2 =新的JLabel(); / /無參數建構子,預設向左對齊
    label2.setBounds(20,70,100,50);
    LABEL2。操作SetIcon(圖標); / /設定圖片
    label2.setToolTipText(“標籤2”);
    cp.add(標記2);
    JLabel的LABEL3 =新JLabel的(圖標,SwingConstants.RIGHT) ; / /指定圖片向右對齊
    label3.setBounds(20,120,100,50);
    label3.setIcon(圖標);
    LABEL3。的setToolTipText(“標籤3”) ;
    cp.add(LABEL3);


可見如果只傳的ImageIcon物件,則預設會置換中對齊(第一個);若是呼叫無參數建構子,則跟文字一樣是置左對齊(第二個);最後一個是指定向右對齊。也可以呼叫的setToolTipText()方法替的JLabel加上提示文字。

JLabel的還有一個建構子可以同時指定文字與圖片,預設圖片在左,文字在右:

JLabel的標籤=新的JLabel(“標籤”圖標,SwingConstants.RIGHT);

測試範例3  :  http://mybidrobot.allalla.com/javatest/JLabel3.zip  [ 看原始碼]

    ImageIcon的圖標=新的ImageIcon(“icon.jpg”);
    JLabel的標籤1 =新的JLabel(“標籤1”,圖標,SwingConstants.LEFT); / /整體向左對齊
    label1.setBounds(20,20,100,50);
    cp.add項(Label);
    JLabel的標記2 =新的JLabel(“標籤2”,圖標,SwingConstants.RIGHT); / /整體向右對齊
    label2.setBounds(20,80,100,50);
    cp.add(標記2);
    JLabel的LABEL3 =新的JLabel(“標籤3”,圖標,SwingConstants.CENTER); / /整體置中對齊
    label3.setBounds(20,140,​​100,50);
    cp.add(LABEL3);
    JLabel的Label4的=新的JLabel(“標籤4”,圖標,SwingConstants.CENTER); / /整體置中對齊
    Label4的。setHorizontalTextPosition(SwingConstants.LEFT); / /文字在圖左(預設為右)
    label4.setBounds(20,200,100,50);
    cp.add(Label4的);
    JLabel的label5 =新的JLabel(“標籤5”,圖標,SwingConstants.RIGHT); / /整體向右對齊
    。label5 setVerticalTextPosition(SwingConstants.TOP) ; / /文字垂直向上
    label5.setBounds(200,20,100,50);
    cp.add(label5);
    JLabel的label6 =新的JLabel(“標籤6”圖標,SwingConstants.RIGHT); / /整體向右對齊
    label6.setVerticalTextPosition(SwingConstants.CENTER); / /文字垂直置中
    label6.setBounds(200,80,100,50);
    cp.add(label6);
    JLabel的label7 =新的JLabel(“標籤7”,圖標,SwingConstants.RIGHT); / /整體向右對齊
    label7.setVerticalTextPosition(SwingConstants.BOTTOM); / /文字垂直向下
    label7.setBounds(200,140,​​100,50);
    cp.add(label7);


此例中可見建構子的第三參數是指文字與圖片的整體對齊方式,兩者擺放方式預設是圖先字後,如果要改變為文先圖後,必須呼叫setHorizo​​ntalTextPosition()方法,傳入SwingConstants.LEFT即可。文字的垂直位置則可以呼叫setVerticalTextPosition()來設定,如標籤5,6,7所示。

以上為JLabel的常用方式。要注意單獨圖片與文字的水平尺位置預設什麼。不同的,文字是靠左,圖片是置中,文圖一起出現時預設是圖先文後,垂直位置均為置中

接著來看文字欄位的JTextField,此元件用來輸入單行文字,其建構子為:

JTextField的文本=新的JTextField();  / /預設為欄位數為0

也可以傳入欄位數(列),指定可輸入的文字寬度,但實際顯示寬度是平均字元寬度(像素)乘以欄位數,但這只有在有版面管理員時才有效果,若自行排版則受到尺寸限制,:

JTextField的文本=新的JTextField(12);  / /指定12欄

可以指定預設文字:

JTextField的文本=新的JTextField(“請輸入帳號”);

或同時指定欄位與預設文字:

JTextField的文本=新的JTextField(“請輸入帳號”,12);

如下列範例4所示:

測試範例4  :  http://mybidrobot.allalla.com/javatest/JTextField1.zip  [ 看原始碼

導入java.awt中的*。;
進口java.awt.event中*。;
進口javax.swing中*。;
進口javax.swing.event中的*。;

公共類JTextField1 {
  JFrame的F;
  公共靜態無效的主要(字串的argv []){
    新JTextField1();  
    }
  公共JTextField1(){
    JFrame.setDefaultLookAndFeelDecorated(真);
    JDialog.setDefaultLookAndFeelDecorated(真);
    F =新的JFrame(“JTextField1”);
    f.setSize(400,300); 
    f.setLocationRelativeTo(NULL); 
    集裝箱CP = f.getContentPane();
    cp.setLayout(NULL);
    JTextField的文本1 =新的JTextField(5);
    text1.setBounds(20,20,100,25);
    cp.add(文本);
    JTextField的文本2 =新的JTextField(15);
    text2.setBounds(20,60,100,25);
    cp.add(文本2);
    JTextField的文本3 =新的JTextField(“請輸入帳號”);
    text3.setBounds(20,100,100,25);
    cp.add(文本3);
    JTextField的文本4 =新的JTextField(“請輸入密碼”,100);
    text4.setBounds(20,140,​​100,25);
    cp.add(文字4);
    f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);     
    f.setVisible(真); 
    f.addWindowListener(新WindowAdapter(){
      公共無效windowClosing(WindowEvent E){
        int結果= JOptionPane.showConfirmDialog(F,
                   “確定要結束程式嗎?”
                   “確認訊息”,
                   JOptionPane.YES_NO_OPTION,
                   JOptionPane.WARNING_MESSAGE);
        如果(結果== JOptionPane.YES_OPTION){System.exit(0);}
        }    
      });
    }
  }



可見自行排版時,欄位設定是沒有作用的。若設定版面管理員就會不同了,如下列範例5所示:

測試範例5  :  http://mybidrobot.allalla.com/javatest/JTextField2.zip  [ 看原始碼


導入java.awt中的*。;
進口java.awt.event中*。;
進口javax.swing中*。;
進口javax.swing.event中的*。;

公共類jTextField2中{
  JFrame的F;
  公共靜態無效的主要(字串的argv []){
    新jTextField2中(); 
    }
  公共jTextField2中(){
    JFrame.setDefaultLookAndFeelDecorated(真);
    JDialog.setDefaultLookAndFeelDecorated(真);
    F =新的JFrame(“jTextField2中”);
    f.setSize(400,300);
    f.setLocationRelativeTo(NULL);
    集裝箱CP = f.getContentPane();
    cp.setLayout(新的FlowLayout());
    JTextField的文本1 =新的JTextField(5);
    cp.add(文本);
    JTextField的文本2 =新的JTextField(15);
    cp.add(文本2);
    JTextField的文本3 =新的JTextField(“請輸入帳號”);
    cp.add(文本3);
    JTextField的文本4 =新的JTextField(“請輸入密碼”,20);
    cp.add(文字4);
    f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);    
    f.setVisible(真);
    f.addWindowListener(新WindowAdapter(){
      公共無效windowClosing(WindowEvent E){
        int結果= JOptionPane.showConfirmDialog(F,
                   “確定要結束程式嗎?”
                   “確認訊息”,
                   JOptionPane.YES_NO_OPTION,
                   JOptionPane.WARNING_MESSAGE);
        如果(結果== JOptionPane.YES_OPTION){System.exit(0);}
        }   
      });
    }
  }

可見在的FlowLayout版面管理員下,欄位設定就有作用了。但在網格佈局下也沒有作用,因為它會把元件撐大到JFrame中之邊界,如下列範例6

測試範例6  :  http://mybidrobot.allalla.com/javatest/JTextField3.zip  [ 看原始碼

    集裝箱CP = f.getContentPane();
    cp.setLayout(新GridLayout的(0,1));
    JTextField的文本1 =新的JTextField(5);
    cp.add(文本);
    JTextField的文本2 =新的JTextField(15);
    cp.add(文本2);
    JTextField的文本3 =新的JTextField(“請輸入帳號”);
    cp.add(文本3);
    JTextField的文本4 =新的JTextField(“請輸入密碼”,20);
    cp.add(文字4);


測試範例7  :  http://mybidrobot.allalla.com/javatest/JTextField4.zip  [ 看原始碼

    集裝箱CP = f.getContentPane();
    / / cp.setLayout(新的BorderLayout()); / /預設即為邊界佈局,不必特別設定
    JTextField的文本1 =新的JTextField(5);
    cp.add(文字,BorderLayout.EAST);
    JTextField的文本2 =新的JTextField(15);
    cp.add(文字2,BorderLayout.WEST);
    JTextField的文本3 =新的JTextField(“請輸入帳號”);
    cp.add(文本3,BorderLayout.SOUTH);
    JTextField的文本4 =新的JTextField(“請輸入密碼”,20);

    cp.add(文本4,BorderLayout.NORTH);



可見邊界佈局下,視窗開啟後,南北方向會撐到視窗寬度,東西方向會則依欄位大小分配。不過視窗縮放後高度會縮到JTextField中的預設高度:



JTextField中有兩個常用的方法的getText()與的setText(字符串文本),可用來存取使用者所輸入之內容,如下列範例8所示: 

測試範例8  :  http://mybidrobot.allalla.com/javatest/JTextField5.zip  [ 看原始碼

導入java.awt中的*。;
進口java.awt.event中*。;
進口javax.swing中*。;
進口javax.swing.event中的*。;

公共類JTextField5 實現的ActionListener {
  JFrame的F;
  JTextField的文本1;
  JButton的B1,B2;
  公共靜態無效的主要(字串的argv []){
    新JTextField5(); 
    }
  公共JTextField5(){
    JFrame.setDefaultLookAndFeelDecorated(真);
    JDialog.setDefaultLookAndFeelDecorated(真);
    F =新的JFrame(“JTextField5”);
    f.setSize(400,300);
    f.setLocationRelativeTo(NULL);
    集裝箱CP = f.getContentPane();
    cp.setLayout(NULL);
    文本1 =新的JTextField(“請輸入帳號”);
    text1.setBounds(20,20,100,25);
    cp.add(文本);
    B1 =新的JButton(“清除”);
    b1.setBounds(140,20,80,25);
    b1.addActionListener(本);
    cp.add(B1);
    B2 =新的JButton(“確定”);
    b2.setBounds(230,20,80,25);
    b2.addActionListener(本);
    cp.add(B2);
    f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);    
    f.setVisible(真);
    f.addWindowListener(新WindowAdapter(){
      公共無效windowClosing(WindowEvent E){
        int結果= JOptionPane.showConfirmDialog(F,
                   “確定要結束程式嗎?”
                   “確認訊息”,
                   JOptionPane.YES_NO_OPTION,
                   JOptionPane.WARNING_MESSAGE);
        如果(結果== JOptionPane.YES_OPTION){System.exit(0);}
        }   
      });
    }
  公共無效的actionPerformed(ActionEvent的E) {
    如果(e.getSource()== b1)的{
        。文本1 的setText(“”) ;
        }
    否則,如果(e.getSource()== b2)的{
        JOptionPane.showMessageDialog(F,
          “您的帳號是”+ 文本1。的getText(),
          “訊息”,JOptionPane.INFORMATION_MESSAGE);     
        }
    }
  }



此例中我們放置了兩個按鈕B1與B2,並實作的ActionListener介面。因為要在的actionPerformed()方法中存取文字欄位的text1,因此這三個元件必須宣告為類別成員。按下“清除”鍵會呼叫的setText(“”)方法清除欄位中的值,而按下“確定”則呼叫的getText()方法取得其值。

下面範例9要測試的JTextField從JComponent的類別繼承而來的另外三個常用方法:切(),複製,粘貼與():

測試範例9 :  http://mybidrobot.allalla.com/javatest/JTextField6.zip  [ 看原始碼

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

public class JTextField6 implements ActionListener {
  JFrame f;
  JTextField text1,text2;
  JButton cut, copy, paste;
  public static void main(String argv[]) {
    new JTextField6();  
    }
  public JTextField6() {
    JFrame.setDefaultLookAndFeelDecorated(true);
    JDialog.setDefaultLookAndFeelDecorated(true);
    f=new JFrame("JTextField6");
    f.setSize(400,300); 
    f.setLocationRelativeTo(null); 
    Container cp=f.getContentPane();
    cp.setLayout(null);
    text1=new JTextField();
    text1.setBounds(20,20,200,25);
    cp.add(text1);
    cut=new JButton("剪下");
    cut.setBounds(20,70,60,25);
    cut.addActionListener(this);
    cp.add(cut);
    copy=new JButton("複製");
    copy.setBounds(90,70,60,25);
    copy.addActionListener(this);
    cp.add(copy);
    paste=new JButton("貼上");
    paste.setBounds(160,70,60,25);
    paste.addActionListener(this);
    cp.add(paste);
    text2=new JTextField();
    text2.setBounds(20,120,200,25);
    cp.add(text2);
    f.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);     
    f.setVisible(true); 
    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) {
    if (e.getSource()==cut) {text1.cut();}
    else if (e.getSource()==copy) {text1.copy();}
    else if (e.getSource()==paste) {text2.paste();}
    }

  }

此例中我們放置了兩個JTextField的:文本1與文本2,前者用來輸入資料以便剪下或複製,後者用來貼上剪下或複製之資料。當以滑鼠選擇部份文字,按剪下鈕時,該子字串會被移到剪貼簿,按下貼上鈕就會出現在下方的文本2文字欄位上,但按下複製鈕時,上方子字串仍在那裡,只是被複製一份到剪貼簿,按貼上就出現在下方欄位上。

以上便是JTextField的常用方法。JPassword跟JTextField的是類似的元件,都是單行文字欄位,差別是JPassword輸入時欄位上是顯示指定的字元(預設是黑色圓點),以保護密碼不被周遭的人偷看(其實?看鍵盤不就一樣會洩漏),如下列範例10所示:

測試範例10 :  http://mybidrobot.allalla.com/javatest/JPasswordField1.zip  [ 看原始碼]


2014年2月22日 星期六

狹義相對論的一些觀念

二哥問我狹義相對論的問題, 一個高速行駛中的火車, 其車前燈射出的光是否會比光速快一些些, 答案是不會, 仍然是光速, 就算火車跑到接近光速, 車燈的速度還是一樣維持在光速, 不是接近兩倍光速. 從維基摘要整理了一些觀念如下 (原文數學很多) :
  1. 由愛因斯坦於 1905 年的論文 "論動體的電動力學" 所提出, 描述平直線性時空下 (即無重力狀態下) 的相對性原理.
  2. 狹義相對論是愛因斯坦對牛頓時空觀 (古典力學) 的修正, 它將古典力學擴展為物體在一切運動速度下的普遍情況. 在遠低於光速的低速運動下 (例如計算天體行星軌道) 兩者近似, 但高速運動中的物體 (例如加速器中高速行進的粒子) 則必須以狹義相對論來修正. 古典力學其實只是狹義相對論的一個特例而已.
  3. 狹義相對論的基本原理為光速不變狹義相對性原理, 光速不會因為光源運動而增減 (心經 : 舍利子, 是諸法空相, 不生不滅, 不垢不淨, 不增不減). 而狹義相對性原理則是認為, 在所有慣性系統中, 物理定律有相同的表達形式. 愛因斯坦還把原本用來解決古典力學與古典電磁學不一致的勞倫茲變換用在狹義相對論的推導中, 以解釋高速運動物體在不同慣性參照系下, 時間與空間座標變換的規律.
  4. 牛頓的時空觀與愛因斯坦的狹義相對論時空觀都認為時間與空間是相依的, 不是互相獨立的. 但兩者的差異在於, 牛頓的時空觀是一個平直非線性的時空, 可用一個三維的速度空間描述, 時間不是獨立於空間的單獨一維, 而是空間座標的自變數. 而狹義相對論的時空觀則是平直線性的, 認為時間與空間應該要用統一的四維空間來描述, 不存在絕對的空間與時間.
  5. 狹義相對論主要的結論是 :
    a. 時間膨脹 :
        觀察高速運動物體, 我們會量測到其時間變慢 (相對時慢). 但反過來, 被觀測者測量我們, 也發現我們的時間變慢了.
    b. 長度收縮 :
        觀察高速運動物體, 我們會量測到他們在運動方向的長度變短了, 但他們自己量卻不變.  但反過來, 被觀測者測量我們, 也發現我們在朝運動方向的長度變短了, 我們自己卻不覺得. (金剛經 : 凡所有相, 皆是虛妄, 若見諸相非相, 則見如來.)
    c. 同時是相對的
        除非兩個事件發生在同一時空點, 否則無法判斷是否絕對同時發生.
        但可以利用兩事件距離中點的信號傳播達成相對同時.
    d. 質量是相對的
        速度增加時, 質量也會增加, 接近光速時, 質量會趨近無限大.
        任何有質量的物體不可能達到光速. 光子質量為 0, 故可達到光速.

參考 :
# 維基百科 : 狹義相對論
# 維基百科 : 勞倫茲變換
# 維基百科 : 質能等價

2014年2月21日 星期五

金錢, 權力, 與傲慢

今日閱讀同事 email 分享, 這篇看了讓人不知道該說甚麼才好 ....

"在美國,3%的大學生願意考公務員;在法國,是5.3%;在新加坡,只有2%;在日本,公務員排在第53位; 在英國,公務員進入20大厭惡職業榜;而在台灣,近四成(40%)想當公務員,逾五(50%)成想考一般行政人員。"

"俄羅斯前總統梅德韋傑夫說過,一個國家的青年,爭著去當公務員,這說明這個國家的腐敗已嚴重透了。歐美國家,最宏偉的建築主要是教堂,因為那裡存放著他們的信仰—博愛、自由、平等;在日本,最奢華的建築是學校,因為那裡存放著他們的信仰—知識、技術、進取在台灣,最宏偉的建築主要是政府大樓、銀行,因為那裡存放著他們的信仰—"金錢、權力、傲慢"。"


2014年2月18日 星期二

Hinet 網頁空間改成 SFTP 上傳

我的忘年之交劉伯伯年前通知我要修改部分網頁內容, 我用 ftp 欲將改好的網頁上傳, 卻發現無法連線, 用檔案總管連線 ftp://ftp.myweb.hinet.net, 密碼敲進去就跳出來, 以為密碼改了, 用劉伯伯的 Hinet 連線密碼也不行 (其實這兩個密碼完全無關), 問 Hinet 客服也是不清不楚, 還要花時間解釋. 今天看到這篇 :

# Hinet 空間無法上傳嗎?FTP 改成 SFTP 的設定方式

原來 Hinet 空間去年就已停止 ftp 上傳, 改用 sftp, 於是下載了 FileZilla 來連線, 發現還是無法連線, 顯示 "認證失敗". 找了一下 Hinet 說明, 原來光是改用 sftp 還不行, 必須先去 Hinet 空間登入後, 四小時內用 sftp 連線才行 :

2013/03/01 起:關閉 FTP,屆時客戶僅能使用 SFTP。使用 SFTP 功能之前,需到本服務首頁執行帳號密碼認證, 每次登入成功後的四小時內,可使用 SFTP 方式登入本服務

果然這樣就能連線 ftp 伺服器更新檔案了.

Hinet 還打算 4/1 起停止計數器與留言板服務 :

系統公告:自2013/10/01起,新申請MyWeb服務的客戶,不再提供:留言板、討論區、網頁計數器…三項服務,並於2014/04/01起停止上述三項服務,建請貴客戶改用本公司「 Xuite隨意窩」相關服務

我已幫劉伯伯申請了夢想家計數器 :

http://www.dreamhome.com.tw/escounter/

等隨意窩申請好就改過去唄.

傳世漢英辭庫

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);