WisdomSoft - for your serial experiences.

6.11 ルックアンドフィール

Java Swing の UI コンポーネントは Java 自身で描画しているため、外観や振る舞い(ルックアンドフィール)がシステムから独立しています。

6.11.1 プラグイン可能なユーザーインタフェース

Swing コンポーネントは、プラットフォームに依存したコードを実行しているのではなく、すべてが Java のグラフィックス機能を用いた描画処理で実現されていることに大きな特徴があります。Java はネイティブなシステムに比べて動作速度を犠牲にしているため、Swing は動作が遅いという批判もありますが、処理速度がネイティブなコードよりも劣っているから、Swing は使えないという意見はあまりにも短絡的です。

むしろ、現在のコンピュータプログラミングは動作速度の最適化よりも、柔軟性や再利用性など、設計の分野における最適化のほうが重要であるということが認められ始めています。だとすれば、Swing は GUI ソフトウェア開発用のライブラリとしては先進的なライブラリの 1 つであると言えます。その最大の理由が、プラグイン可能なルックアンドフィールの存在です。

Swing のコンポーネントは描画処理だけで実現されているため、コンポーネントを描画するコードを置き換えるだけで、ユーザーインタフェースを置き換えることができてしまうのです。理論的には Windows で Mac OS のルックアンドフィールを表現したり、その逆を実現することも可能です。ただし、これに関しては両社が認めていないので技術的には可能ですが、権利的な理由でできません。

Swing アプリケーションであれば、プログラムの実行中にルックアンドフィールを変更することも可能です。ユーザーの気分で、Windows のルックアンドフィールに飽きたので Unix のルックアンドフィールに変更するという設定も Swing であれば簡単に実装できてしまいます。もちろん、仕組みを熟知すれば、独自のルックアンドフィールを開発することも不可能ではありません。

Swing アプリケーションのルックアンドフィール全体を管理しているのは javax.swing.UIManager クラスです。UIManager クラスはルックアンドフィールを管理するための静的なメソッドを提供するクラスです。

javax.swing.UIManager クラス
java.lang.Object
  |
  +--javax.swing.UIManager
public class UIManager extends Object implements Serializable

UIManager クラスのメソッドは static で宣言されているクラスメソッドなので、UIManager をインスタンス化する必要はありません。ルックアンドフィールを変更するには、UIManager クラスの setLookAndFeel() メソッドを使います。現在のルックアンドフィールは getLookAndFeel() メソッドから取得することができます。

UIManager クラス setLookAndFeel() メソッド
public static void setLookAndFeel(LookAndFeel newLookAndFeel) throws UnsupportedLookAndFeelException
public static void setLookAndFeel(String className)
	throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
UIManager クラス getLookAndFeel() メソッド
public static LookAndFeel getLookAndFeel()

これらのメソッドに設定するのは、ルックアンドフィールを実装している javax.swing.LookAndFeel クラスを実装しているオブジェクトです。 LookAndFeel クラスは抽象クラスで、このクラスの実装でコンポーネントの特性が定義されます。

javax.swing.LookAndFeel クラス
java.lang.Object
  |
  +--javax.swing.LookAndFeel
public abstract class LookAndFeel extends Object

Swing がデフォルトで用意しているルックアンドフィールは javax.swing.plaf.metal.MetalLookAndFeel クラスだけです。このルックアンドフィールは Metal というコード名で管理される Java 標準のルックアンドフィールです。Metal は、Java Swing がサポートされるあらゆるプラットフォームで利用できるクロスプラットフォームのルックアンドフィールなのです。

javax.swing.plaf.metal.MetalLookAndFeel クラス
java.lang.Object
  |
  +--javax.swing.LookAndFeel
        |
        +--javax.swing.plaf.basic.BasicLookAndFeel
              |
              +--javax.swing.plaf.metal.MetalLookAndFeel
public class MetalLookAndFeel extends BasicLookAndFeel

Java が正式にサポートしているルックアンドフィールは Metal だけですが、UIManager クラスの setLookAndFeel() メソッドは、文字列からクラスを検索するメソッドもオーバーライドされているため、プログラムの実行時にルックアンドフィールを検索するような処理も難しくはありません。

UIManager クラスは、インスタンスやクラス名に依存せずに、必要なルックアンドフィールにアクセスできるように、Swing を実行しているシステムのルックアンドフィールを返す getSystemLookAndFeelClassName() メソッドと、クロスプラットフォーム用のルックアンドフィールを返す getCrossPlatformLookAndFeelClassName() メソッドを提供しています。

UIManager クラス getSystemLookAndFeelClassName() メソッド
public static String getSystemLookAndFeelClassName()
UIManager クラス getCrossPlatformLookAndFeelClassName() メソッド
public static String getCrossPlatformLookAndFeelClassName()

クロスプラットフォーム用のルックアンドフィールとは Java のルックアンドフィールなので、通常は Metal になります。システムのルックアンドフィールとは、プログラムを実行しているネイティブシステムのルックアンドフィールなので Windows で実行すれば Windows の、Mac OS で実行すれば Mac OS のルックアンドフィールとなります。

Windows のルックアンドフィールは、通常 com.sun.java.swing.plaf.windows.WindowsLookAndFeel クラス、Mac OS X のルックアンドフィールは apple.laf.AquaLookAndFeel クラス、UNIX のルックアンドフィールは com.sun.java.swing.plaf.motif.MotifLookAndFeel クラスで提供されています。ただし、Windows と Mac OS のルックアンドフィールは、他のプラットフォームで利用することはできません。UNIX の Motif はどのプラットフォームでも利用することができます。

UIManager の現在のルックアンドフィールを変更しても、すでにインスタンス化されているコンポーネントの設定は変更されません。コンポーネントの描画や動作を定義しているユーザーインタフェースの委譲先を更新するには JComponent クラスの updateUI() メソッドを呼び出さなければなりません。

JComponent クラス updateUI() メソッド
public void updateUI()

各コンポーネントのインスタンスが参照しているユーザーインタフェースの委譲先を異なるルックアンドフィールに対応させれば、様々なルックアンドフィールのコンポーネントを同時に表示することもできますが、そのようなデザインはユーザーを無意味に混乱させるだけでしょう。ルックアンドフィールを変更した場合は、アプリケーションが表示しているコンポーネントをすべて更新するべきです。幸い、SwingUtilities クラスが、コンテナに含まれているすべてのコンポーネントの updateUI() 処理を行ってくれる updateComponentTreeUI() メソッドを提供しています。

SwingUtilities クラス updateComponentTreeUI() メソッド
public static void updateComponentTreeUI(Component c)

アプリケーションウィンドウをこのメソッドに渡せば、ウィンドウに含まれているすべてのコンポーネントの updateUI() を呼び出してくれます。これで、ウィンドウ全体のルックアンドフィールを一度に更新することができます。

コード1
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Test extends JFrame implements ItemListener {
	public static void main(String args[]) {
		JFrame win = new Test();
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		win.setBounds(10 , 10 , 400 , 300);
		win.show();
	}

	JComboBox comboBox = new JComboBox();
	public Test() {
		JPanel panel = new JPanel();
		panel.setLayout(new FlowLayout());
		panel.add(new JButton("JButton"));
		panel.add(new JLabel("JLabel"));
		panel.add(new JCheckBox("JCheckBox"));
		panel.add(new JRadioButton("JRadioButton"));
		panel.add(new JSlider());
		panel.add(new JScrollBar());
		panel.add(new JProgressBar());

		comboBox.addItem(UIManager.getSystemLookAndFeelClassName());
		comboBox.addItem(UIManager.getCrossPlatformLookAndFeelClassName());
		comboBox.addItem("com.sun.java.swing.plaf.motif.MotifLookAndFeel");

		comboBox.addItemListener(this);

		getContentPane().add(comboBox , BorderLayout.NORTH);
		getContentPane().add(panel , BorderLayout.CENTER);
	}
	public void itemStateChanged(ItemEvent e) {
		if (e.getStateChange() != ItemEvent.SELECTED) return;

		try { 
			UIManager.setLookAndFeel(
				comboBox.getSelectedItem().toString()
			);
		 }
		catch(Exception err) {
			JOptionPane.showMessageDialog(
				this , "ルックアンドフィールの変更に失敗しました" ,
				"L&F 変更エラー" , JOptionPane.ERROR_MESSAGE
			);
		}
		SwingUtilities.updateComponentTreeUI(this);
	}
}
実行結果
コード1 実行結果

コード1を実行すれば、Swing のプラグイン可能なルックアンドフィールがどれだけ柔軟で強力なものなのかを体験することができるでしょう。ウィンドウの上部に表示されているコンボボックスには、システムのネイティブなルックアンドフィール、Java ルックアンドフィール、そして Motif ルックアンドフィールが項目として含まれています。これらのいずれかを選択すると、UIManager にルックアンドフィールが設定され、ウィンドウのコンポーネントのユーザインタフェース委譲先が更新されます。

つまり、このプログラムではコンボボックスの項目を選択するだけで、アプリケーションの実行途中でユーザーインタフェースを変更することができるのです。個々のルックアンドフィールによって、コンポーネントの動作も多少異なりますが、基本的な部分では互換性があります。

図1
図1 Windows デフォルト
図2 Windows Metal
図2 Windows Metal
図3 Windows Motif
図3 Windows Motif
図4 Mac OS X デフォルト
図4 Mac OS X デフォルト
図5 Mac OS X Metal
図5 Mac OS X Metal
図6 Mac OS X Motif
図6 Mac OS X Motif

図1から図3までは Windows で、図4から図6までは Mac OS X でコード1を実行し、各ルックアンドフィールで表示したものです。同じルックアンドフィールでも、システムの違いで多少異なる部分がありますが、基本的な動作は同じです。

例えば、アプリケーションが常に Metal ルックアンドフィールを選択していれば、ユーザーは Windows でも、Mac OS でも、UNIX でも、システムに関係なく同じ操作感でアプリケーションを制御することができます。サーバー関係の仕事を担当している技術者など、多くのシステムを同時に利用する機会が多い利用者であれば、システムを越えた互換性は歓迎できるものがあるでしょう。

ルックアンドフィールが異なれば、同じプログラムを実行していても、コンポーネントの動作に多少の違いが生まれる可能性があります。本書では、今後も Java 標準の Metal ルックアンドフィールを想定して解説します。