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))だと色が変わってくれないのがちょっと惜しい。