2014年2月8日 星期六

Java Pluggable Look and Feel

Pluggable Look and Feel (外掛式感視) 是Java Swing相當有趣的一項功能,可依不同需求改變Java Swing應用程式的外觀感覺,內建有Metal、Motif與Microsoft Windows Look and Feel,JDK 1.4與JDK 6.0 Update 10並分別新增GTK+與Nimbus Look and Feel,另外Open Source亦提供不同樣式的Look and Feel API以供使用,若仍不能滿足需求,亦可藉由Java Swing所提供的基礎類別開發屬於自己的Look and Feel。

Java SE內建以下Look and Feel類別:
  • Metal:javax.swing.plaf.metal.MetalLookAndFeel
  • CDE/Motif:com.sun.java.swing.plaf.motif.MotifLookAndFeel
  • Windows:com.sun.java.swing.plaf.windows.WindowsLookAndFeel
  • Classic:com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel
  • Nimbus:com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel
  • GTK+:com.sun.java.swing.plaf.gtk.GTKLookAndFeel
  • Mac:com.sun.java.swing.plaf.mac.MacLookAndFeel
上述類別皆繼承自javax.swing.LookAndFeel抽象類別,其中:
  • Metal Look and Feel又稱為Java Look and Feel,為Java預設的跨平臺Look and Feel。
  • Nimbus Look and Feel為JDK 6.0 Update 10新增之Look and Feel,亦稱為SwingSet3 Nimbus Look and Feel,由Swing Labs所開發,其外觀感覺類似於Microsoft Windows Vista。
  • GTK+ Look and Feel為JDK 1.4.2新增之Look and Feel,支援Sun Solaris或Linux with GTK+ 2.2的作業系統。
  • Mac Look and Feel支援Apple MacOS作業系統,又稱為Aqua Look and Feel,不支援Microsoft Windows作業系統。
  • WindowsLookAndFeel類別自JDK 1.4.2已修改為適用於Microsoft Windows XP作業系統的Look and Feel,而Microsoft Windows XP之前的作業系統則是預設為WindowsClassicLookAndFeel類別,亦稱為Windows Classic Look and Feel。兩者差別在於類別中會先以System.getProperty()取得作業系統版本的系統屬性值,以判斷Microsoft Windows的作業系統版本
Look and Feel主要是由javax.swing.UIManager類別處理,欲改變Java Swing的Look and Feel,可使用UIManager類別的setLookAndFeel()方法設定,例如:


try {
  // 設定Metal Look and Feel
  UIManager.setLookAndFeel(  
    new javax.swing.plaf.metal.MetalLookAndFeel());
}
catch (UnsupportedLookAndFeelException e) {
  ...
}


或者是


try {
  // 設定Metal Look and Feel
  UIManager.setLookAndFeel(
    "javax.swing.plaf.metal.MetalLookAndFeel");
}
catch (ClassNotFoundException e) {
  ...
}
catch (InstantiationException e) {
  ...
}
catch (IllegalAccessException e) {
  ...
}
catch (UnsupportedLookAndFeelException e) {
  ...
}


由於作業系統並不一定支援所指定的Look and Feel,因此在執行setLookAndFeel()方法之前,可使用LookAndFeel抽象類別的isSupportedLookAndFeel()方法判斷作業系統是否支援,例如以下為判斷作業系統是否支援GTK+ Look and Feel:


import javax.swing.*;
import javax.swing.UIManager;
import com.sun.java.swing.plaf.gtk.GTKLookAndFeel;
...

GTKLookAndFeel lnf = new GTKLookAndFeel();
  
try {
  if (lnf.isSupportedLookAndFeel())
    UIManager.setLookAndFeel(
      "com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
  else
    System.out.println("系統不支援GTK+ Look and Feel.");
}
catch (Exception e) {
  ...
}


此外通常將Look and Feel的設定置於Java程式之main()方法中,例如:


import javax.swing.plaf.metal.MetalLookAndFeel;
...

public static void main(String[] args) {
  try {
    UIManager.setLookAndFeel(
      "javax.swing.plaf.metal.MetalLookAndFeel");
  }
  catch (Exception e) {
    ...
  }
  ...
}


若在Java Applet中,則可加入以下的程式片斷:


import javax.swing.plaf.metal.MetalLookAndFeel;
...

static {
  try {
    UIManager.setLookAndFeel(
      "javax.swing.plaf.metal.MetalLookAndFeel");
  }
  catch (Exception e) {
    ...
  }
  ...
}


或者是


import javax.swing.plaf.metal.MetalLookAndFeel;
...

public class ... extends javax.swing.JApplet {
  public void init() {
    try {
      UIManager.setLookAndFeel(
        "javax.swing.plaf.metal.MetalLookAndFeel");
    }
    catch (Exception e) {
      ...
    }
    ...
  }
}


此外可配合選單讓使用者自行選擇,但在執行階段變更Look and Feel,需使用SwingUtilities類別的updateComponentTreeUI()方法,請參考以下範例。範例以選單的方式選擇Look and Feel,待選擇之後,除了以setLookAndFeel()方法設定之外,並需以SwingUtilities類別的updateComponentTreeUI()方法更新


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

public class LookandFeelDemo extends javax.swing.JFrame 
  implements ActionListener {

  JRadioButtonMenuItem[] jrbmenuitem = new JRadioButtonMenuItem[7];

  String item[] = {"Metal", "CDE/Motif", "Windows XP", 
    "Windows Classic", "Nimbus", "GTK+", "Mac"};

  String classname[] = {
    "javax.swing.plaf.metal.MetalLookAndFeel", 
    "com.sun.java.swing.plaf.motif.MotifLookAndFeel", 
    "com.sun.java.swing.plaf.windows.WindowsLookAndFeel", 
    "com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel", 
    "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel", 
    "com.sun.java.swing.plaf.gtk.GTKLookAndFeel",
    "com.sun.java.swing.plaf.mac.MacLookAndFeel"};

  // Main method
  public static void main(String[] args) {
    UIManager.put("swing.boldMetal", Boolean.FALSE);

    try {
      // 設定Metal Look and Feel 
      UIManager.setLookAndFeel(
        "javax.swing.plaf.metal.MetalLookAndFeel");
    }
    catch(Exception e) {
      e.printStackTrace();
    }
    
    new LookandFeelDemo();
  }
  
  // 建構函式
  // 測試用
  public LookandFeelDemo() {
    super("Look and Feel Demo");

    // 建立選單列
    JMenuBar jmenubar = createJMenuBar();

    // 定義視窗使用者介面之選單列
    this.setJMenuBar(jmenubar);
    ...
  }

  private JMenuBar createJMenuBar() {
    // 建立選單列
    JMenuBar jmenubar = new JMenuBar();

    // 建立選單
    JMenu jmenuFile = new JMenu("File");

    // 建立選單項目
    JMenu jmenuLF = new JMenu("L & F");

    // 建構群組
    ButtonGroup group = new ButtonGroup();
    
    for (int i=0; i<item.length; i++) {
      // 設定選項按鈕選單項目
      jrbmenuitem[i] = new JRadioButtonMenuItem(item[i]);
      
      // 設定是否啟用選單項目
      jrbmenuitem[i].setEnabled(isLookAndFeelSupported(classname[i]));

      jmenuLF.add(jrbmenuitem[i]);
      group.add(jrbmenuitem[i]);
      
      // 註冊ActionListener
      jrbmenuitem[i].addActionListener(this);
     
      if (i==0)
        jrbmenuitem[i].setSelected(true);
    }

    // 新增選單至選單列
    jmenubar.add(jmenuFile);
    jmenubar.add(jmenuLF);

    return jmenubar;
  }

  private boolean isLookAndFeelSupported(String lnfname) {
    try { 
      Class lnfclass = Class.forName(lnfname);

      javax.swing.LookAndFeel lnf = 
        (LookAndFeel)(lnfclass.newInstance());
      
      // 判斷作業系統是否支援Look and Feel
      return lnf.isSupportedLookAndFeel();
    } 
    catch(Exception e) { 
      return false;
    }
  }

  public void actionPerformed(ActionEvent e) {
    try {
      for (int i=0; i<item.length; i++) {
        if(e.getActionCommand().equals(item[i])) { 
          // 設定Look and Feel
          UIManager.setLookAndFeel(classname[i]);

          this.setTitle(item[i] + " L & F");
        }
      }      
    }
    catch(Exception ex) {}
    
    // 執行階段變更Look and Feel
    SwingUtilities.updateComponentTreeUI(this);
  }
}


【執行結果】
除了使用UIManager類別的setLookAndFeel()方法設定Java Swing使用者介面的Look and Feel之外,第二種方法則是使用java直譯器的屬性設定,此屬性值為swing.defaultlaf,其語法為:

    java -Dswing.defaultlaf=[Look and Feel類別名稱] [Java類別名稱]

例如設定Metal Look and Feel:

    java -Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel LookandFeelDemo

最後一種方式則是直接指定JRE的swing.properties屬性設定檔,以Microsoft Windows為例,假設JRE目錄為C:\Program Files\Java\jre7,則在此目錄的lib下建立一swing.properties屬性設定檔,其內容為:

    # Swing properties
    swing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel

以上三種方法均可設定Look and Feel,Java將依序以下列條件決定Look and Feel:
  • 若在程式中以UIManager類別的setLookAndFeel()方法設定之類別存在並執行成功,則Java以此為程式的Look and Feel。
  • 若上述的類別不存在或程式執行失敗,則以Java直譯器的swing.defaultlaf屬性設定為程式的Look and Feel。
  • 若上述的swing.defaultlaf屬性不存在,則以swing.properties屬性設定檔的設定為程式的Look and Feel。
  • 若上述的設定均不存在,則以Java預設的跨平臺Look and Feel為程式的Look and Feel,則Metal Look and Feel。
【參考資料】

[1] Java Platform, Standard Edition 7 API Specification.
[2] 黃嘉輝,深入研究Java Swing (第二版)。


© Chia-Hui Huang

沒有留言:

張貼留言