カスタムメニュー
メニュー要素とマネージャ
Swing のメニューは、軽量コンポーネントで表現されているため
この恩恵はコンポーネント開発者が直接授かることができます
軽量コンポーネントでメニューが構成されているということは
すなわち、開発者が独自のメニューを作成することができることを表します
最も簡単な独自メニューは、コンポーネントをメニュー項目とすることですが
この場合、メニュー項目独自の動作を実装することができません
例えば、マウスカーソルが上に移動するとハイライトされるというような動作です
そこで Swing のメニュー項目となるコンポーネントは
javax.swing.MenuElement インターフェイスを実装します
これを実装することによって、メニュー項目として管理の対象となるのです
public interface MenuElement
極めて重要なのは、メニュー項目はコンポーネント単位で管理されるのではなく
より抽象化された要素という単位で管理される所に注目してください
このインターフェイスは、実装するべきメソッドを5つ用意しています
まず、描画するべきコンポーネントを MenuElement.getComponent() で返します
public Component getComponent()
これは、必ずしも MenuElement がコンポーネントである必要がないことを意味しますが
通常は、MenuElement をコンポーネントのサブクラスに実装させ
このメソッドは、MenuElement を実装する自分自身を返すという方法が用いられます
MenuElemnet.getSubElements() メソッドはサブ要素の配列を返します
これは、サブ要素が含まれる場合に配列として要求元に渡します
public MenuElement[] getSubElements()
MenuElement の実装が、他の MenuElement を抱合している場合に使いますが
通常はサブ要素を持つことはないので、空の配列を返します
ただし、null を返してはいけないので注意する必要があります
MenuElement.processMouseEvent() はマウスイベントを
MenuElement.processKeyEvent() はキーイベントを処理します
public void processMouseEvent(
MouseEvent event , MenuElement[] path ,
MenuSelectionManager manager
)
public void processKeyEvent(
KeyEvent event , MenuElement[] path ,
MenuSelectionManager manager
)
event は受け取り側要素のコンポーネントであるソースを伴ったイベント情報を
path は受け取り側の要素自身が含まれるメニュー階層内の受け取り側要素のパス
manager は選択マネージャを指定します
event と path を用いれば、イベント発生元の情報などを得ることができます
注目するべきは javax.swing.MenuSelectionManager クラスです
public class MenuSelectionManager extends Object
このクラスは、メニュー階層での選択を所有、管理します
コンポーネント開発者は、このクラスのインスタンスを作成する必要はありません
メニュー要素を選択するには、この MenuSelectionManager に要求します
選択は MenuSelectionManager.setSelectedPath() メソッドで行い
選択されている要素は MenuSelectionManager.getSelectedPath() で得られます
public void setSelectedPath(MenuElement[] path)
public MenuElement[] getSelectedPath()
path には、選択する要素の配列を指定します
getSelectedPath() メソッドは、選択されているようその配列を返します
また、要素のコンポーネントは、マウスイベントをマネージャに通知し
マネージャが適切な通知先の要素にイベントを通達できつようにする必要があります
これは MenuSelectionManager.processMouseEvent() を呼び出します
public void processMouseEvent(MouseEvent event)
event にはコンポーネントで発生したマウスイベントをそのまま渡します
要素に関連付けられたコンポーネントのマウスイベントをキャッチし
その引数を、そのままこのメソッドに渡せば適切に動作します
MenuSelectionManager のインスタンスを保有していない場合は
MenuSelectionManager.defaultManager() メソッドから取得できます
public static MenuSelectionManager defaultManager()
このメソッドは、デフォルトの選択マネージャを返します
これで、要素とコンポーネント、そしてイベントの一連の関係を築くことができます
抽象的な要素という概念とコンポーネントが分離しているため、少し難しいのですが
それらの関係はメニューが選択マネージャを通して管理してくれるので
コンポーネントの開発者は、その設計手順を覚えておけばよいでしょう
最後に、MenuElement.menuSelectionChanged() を実装しましょう
このメソッドは、メニュー選択で要素が追加または削除されたときに
メニュー選択マネージャによって呼び出されます
public void menuSelectionChanged(boolean isIncluded)
isIncluded は、要素の選択状態を指定します
この値が true ならば、要素は選択されています
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Test extends JApplet {
public void init() {
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Pure Girls");
menu.add(new ButtonElement("Kitty on your lap"));
menu.add(new ButtonElement("Tokyo mew mew"));
menuBar.add(menu);
setJMenuBar(menuBar);
}
}
class ButtonElement extends JButton implements MenuElement {
ButtonElement(String text) {
super(text);
setBorder(null);
}
public Component getComponent() { return this; }
public MenuElement[] getSubElements() { return new MenuElement[0]; }
public void menuSelectionChanged(boolean isIncluded) {
if(isIncluded) this.setForeground(Color.RED);
else this.setForeground(Color.BLACK);
repaint();
}
public void processKeyEvent(
KeyEvent event , MenuElement[] path ,
MenuSelectionManager manager) {}
protected void processMouseEvent(MouseEvent e) {
super.processMouseEvent(e);
MenuSelectionManager.defaultManager().processMouseEvent(e);
}
public void processMouseEvent(
MouseEvent event , MenuElement[] path ,
MenuSelectionManager manager) {
if (event.getID() == MouseEvent.MOUSE_CLICKED)
manager.setSelectedPath(null);
else manager.setSelectedPath(path);
}
}
このプログラムは MenuElement を実装する ButtonElement を作成しています
このコンポーネント(要素)は、選択されるとテキストの色を赤に変更します
注目してほしいのは Component.processMouseEvent() のオーバーライドです
まず、通常のマウスイベントを発生させるために super.processMouseEvent() を呼び出し
次に、選択マネージャに適切な要素を選択させるために
MenuSelectionManager.processMouseEvent() を呼び出して通知を行っています
この結果、選択マネージャはコンポーネントのイベント発生を認識し
これに関連した要素にイベントを通達します
ButtonElement の MenuElement.processMouseEvent() では
イベントがクリックならば選択を解除し、メニューを終了させます
そうでなければ、発生元の要素を選択状態にするようにマネージャに通達します