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