S-JIS[2007-02-02/2007-02-06] 変更履歴

JMenuBar(Swing)

JMenuBarは、メニューバー。

子を持つメニューはJMenuで定義し、実際のメニューはJMenuItemで定義する。


メニューの例
リスナー使用 アクション使用
書式(O) 初期化 初期化
太字(B)
斜体(I)
初期化実行
初期化実行
初期化実行
初期化(実行)

リスナーを使用する例

どこかのクラスにリスナーを実装しておき、メニュー作成時にメニューにリスナーを登録する
メニューが選択されると、リスナーが呼ばれる。

public class HteFrame extends JFrame implements ActionListener {
〜
	/**
	 * メニューバー初期化
	 */
	private void initMenuBar() {

		JMenu font = new JMenu("書式(O)");
		font.setMnemonic('O');
		{
			JMenuItem font_b = new JMenuItem("太字(B)");
			font_b.setMnemonic('B');
			font_b.setActionCommand("font_bold");
			font_b.addActionListener(this);

			JMenuItem font_i = new JMenuItem("斜体(I)");
			font_i.setMnemonic('I');
			font_i.setActionCommand("font_italic");
			font_i.addActionListener(this);

			font.add(font_b);
			font.add(font_i);
		}

		JMenuBar bar = new JMenuBar();
		bar.add(font);
		setJMenuBar(bar);
	}
	/**
	 * アクション実行
	 */
	public void actionPerformed(ActionEvent e) {
		String cmd = e.getActionCommand();

		if (cmd.equals("font_bold")) {
			Component[] cs = getContentPane().getComponents();
			for (int i = 0; i < cs.length; i++) {
				if (cs[i] instanceof JEditorPane) {
					JEditorPane editor = (JEditorPane) cs[i];
					Document doc = editor.getDocument();
					int ss = editor.getSelectionStart();
					int se = editor.getSelectionEnd();
					if (ss < se) changeBold((HTMLDocument) doc, ss, se);
					break;
				}
			}
			return;
		}

		if (cmd.equals("font_italic")) {
			〜
			return;
		}
	}
}

このやり方だと、コマンド文字列を1つ1つ判定しなければいけないので、数が多くなると馬鹿みたいなプログラムになる。
if文を羅列する代わりに、“コマンドの文字列”と“実行するクラス”をマップに入れて管理するという手も考えられるが、それはアクションを使う方法のSwing側の内部実装とほぼ同じなので、素直にアクションを使った方がスマートになる。

あるいは、上記の例ではリスナーの実装を1つのクラスだけで行ったが、コマンド毎に別々にリスナー実装クラスを作るという手も考えられる。
その場合は、目的のペイン(ここではJEditorPane)を取得する方法を別途作らないといけないだろうけど。


アクションを使用する例

JEditorPaneで文字の属性を操作する為のActionが用意されている。太字にするアクションクラスもあるので、それを使ってみる。

public class HteFrame extends JFrame {
〜
	/**
	 * メニューバー初期化
	 */
	private void initMenuBar() {

		JMenu font = new JMenu("書式(O)");
		font.setMnemonic('O');
		{
			JMenuItem font_b = new JMenuItem(new StyledEditorKit.BoldAction());
			font_b.setText("太字(B)");
			font_b.setMnemonic('B');

			JMenuItem font_i = new JMenuItem(new StyledEditorKit.ItalicAction());
			font_i.setText("斜体(I)");
			font_i.setMnemonic('I');

			font.add(font_b);
			font.add(font_i);
		}

		JMenuBar bar = new JMenuBar();
		bar.add(font);
		setJMenuBar(bar);
	}
}


アクションを自分で実装したいときは、以下のようにする。

public class HteFrame extends JFrame {
〜
	/**
	 * メニューバー初期化
	 */
	private void initMenuBar() {
〜
		JMenuItem font_b = new JMenuItem(new BoldAction());
		font_b.setText("太字(B)");
		font_b.setMnemonic('B');
〜
	}
}

BoldAction.java:

public class BoldAction extends StyledEditorKit.StyledTextAction {

	public BoldAction() {
		super("font_bold");
		putValue(Action.SHORT_DESCRIPTION, "font bold");	//ツールヒント
	}

	/**
	 * アクション実行
	 */
	public void actionPerformed(ActionEvent e) {
		JEditorPane editor = getEditor(e);
		if (editor != null) {
			Document doc = editor.getDocument();
			int ss = editor.getSelectionStart();
			int se = editor.getSelectionEnd();
			if (ss < se) HteFrame.changeBold((HTMLDocument) doc, ss, se);
		}
	}
}

StyledTextActionにはJEditorPaneを返すメソッドがあるので便利。
同じアクションを使ってツールバーのボタンを作ると、アクションのenabledを切り替えるだけでメニューにもツールバーのボタンにも反映される。[/2007-02-06]


アクセラレータキーの設定

アクセラレータキー(ショートカットキー)を設定するのも簡単。
アクセラレータキーを割り当てると、メニューにもそのキーが表示されるようになる。

	/**
	 * メニューバー初期化
	 */
	private void initMenuBar() {
〜
		JMenuItem font_b = new JMenuItem(new BoldAction());
		font_b.setText("太字(B)");
		font_b.setMnemonic('B');
		font_b.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.CTRL_MASK));
〜
	}
}

この例では「Ctrl+B」を割り当てている。


メニューバーの右端にボタンを追加

メニューバーの右端にボタンを付ける方法(JDK1.4)。[2007-02-06]

タブを使っているアプリケーションには、ウィンドウ全体の「閉じる」ボタン(×ボタン)の他に、現在のタブのみを閉じるボタンが付いているものがある。
そういう×ボタンを、本来ならタブの行の右端に付けたいと思ったのだが、スクロールタイプだとスクロール用のボタンがその位置に来るため、扱いが面倒そうだったので、メニューバーの右端に×ボタンを追加する方法を考えた。

※メニューバーのコンポーネントにボタンを追加するのは、外観(LookAndFeel)がWindowsのときでないと使えないかもしれない
 (標準スタイルではメニューの描画でコンポーネントをJMenuにキャストする箇所があって、当然JButtonはキャストできなくて例外が発生する)

	/**
	 * メニューバー初期化
	 */
	private void initMenuBar() {
〜
		//特定のタブのみを閉じる×ボタン
		JButton btn = new JButton(new 閉じるアクション());
		Dimension sz = new Dimension(16, 15);
		btn.setMaximumSize(sz);
		btn.setMinimumSize(sz);
		btn.setIcon(WindowsIconFactory.createFrameCloseIcon());  …Windows用 ×印のアイコン

		JMenuBar bar = new JMenuBar();
		bar.setLayout(new ButtonRgihtMenuLayout(this));
〜
		bar.add(btn);
		setJMenuBar(bar);
	}
}
	/**
	 * メニューバーで、ボタンを一番右に配置するレイアウトマネージャー
	 * <p>
	 * JMenuBarに全JMenuをaddしてからJButtonをaddすること。
	 * </p>
	 */
	class ButtonRgihtMenuLayout extends DefaultMenuLayout {

		private static final long serialVersionUID = 8668767819146592040L;

		/**
		 * コンストラクター
		 */
		public ButtonRgihtMenuLayout(Container target) {
			super(target, DefaultMenuLayout.LINE_AXIS);
		}

		/**
		 * レイアウト計算
		 */
		public void layoutContainer(Container target) {
			super.layoutContainer(target); //各メニュー(ボタンも含んでしまう)の位置を計算

			int tx = target.getWidth() - target.getInsets().right;

			// 全コンポーネントを見て、ボタンだったら右に寄せる
			for (int i = target.getComponentCount() - 1; i >= 0; i--) {
				Component c = target.getComponent(i);
				if (c instanceof JButton) {
					JButton b = (JButton) c;
					int mx = b.getMaximumSize().width;
					if (b.getWidth() > mx) {
						b.setSize(mx, b.getHeight());
					}
					int x = tx - b.getWidth();
					b.setLocation(x, b.getY());
					tx = x;
				}
			}
		}
	}

Windows外観で使われる×ボタンのアイコンは、アクションが使えない状態(setEnabled(false))だと色が変わってくれないのがちょっと惜しい。


Swing目次へ戻る / Java目次へ戻る
メールの送信先:ひしだま