Actionクラスで一括管理

ActionListenerとAbstactAction

メニューアイテムやボタン等の設定方法として、一番よく知られているのはActionListenerを実装することかもしれません。
しかし、メニューアイテムやボタンにActionListener、テキスト、ツールチップ等を設定して、それらをメニュー、ツールバー、ポップアップメニューに追加していく作業は非常に面倒なものです。そこで、AbstractActionを継承したクラスを使って、これらの設定をまとめてやってしまう方法を紹介しましょう。
Actionクラスという概念はJavaには有りませんが、今回参考にさせていただいた『Java GUIプログラミング〈Vol.1〉さらにパワーアップしたSwing』(大村 忠史 著)にならって、AbstractActionを継承しているクラス(それゆえにActionインターフェースを実装するクラス)を便宜上「Actionクラス」と呼ばせてもらいます。

メニュー、ツールバー、ポップアップメニューをまとめて管理する

今回はテキストエディタのようなものを作ってみます。といってもコピー、ペーストの機能だけを持つ簡単なものです。

ActionTest.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ActionTest extends JFrame {
    JTextArea textArea;
    AbstractAction copyAction;
    AbstractAction pasteAction;
    JPopupMenu popupMenu;
    
    public static void main(String[] args){
        ActionTest f=new ActionTest("Actionクラスで一括管理");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(400,300);
        f.setVisible(true);
    }
    
    public ActionTest(String title){
        super(title);
        //Actionクラスの初期化
        copyAction=new CopyAction();
        pasteAction=new PasteAction();
        //コンポーネントの生成
        JPanel panel=(JPanel)getContentPane();
        //テキストエリア
        textArea=new JTextArea();
        textArea.addMouseListener(new CheckPopup());
        panel.add(textArea, BorderLayout.CENTER);
        //メニューバー
        JMenuBar menuBar=new JMenuBar();
        setJMenuBar(menuBar);
        //編集メニュー
        JMenu editMenu=new JMenu("編集");
        menuBar.add(editMenu);
        editMenu.add(copyAction);
        editMenu.add(pasteAction);
        //モードメニュー
        JMenu modeMenu=new JMenu("モード");
        menuBar.add(modeMenu);
        ButtonGroup bg=new ButtonGroup();
        JRadioButtonMenuItem editableMI=new JRadioButtonMenuItem("編集可");
        editableMI.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                pasteAction.setEnabled(true);//貼付け可
                textArea.setEditable(true);
            }
        });
        modeMenu.add(editableMI);
        bg.add(editableMI);
        JRadioButtonMenuItem uneditableMI=new JRadioButtonMenuItem("編集不可");
        uneditableMI.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e){
                pasteAction.setEnabled(false);//貼付け不可
                textArea.setEditable(false);
            }
        });
        modeMenu.add(uneditableMI);
        bg.add(uneditableMI);
        editableMI.setSelected(true);
        //ツールバー
        JToolBar toolBar=new JToolBar();
        panel.add(toolBar, BorderLayout.NORTH);
        toolBar.add(copyAction);
        toolBar.add(pasteAction);
        //ポップアップメニュー
        popupMenu=new JPopupMenu();
        popupMenu.add(copyAction);
        popupMenu.add(pasteAction);
    }
    //ポップアップメニューの表示用
    class CheckPopup extends MouseAdapter {
        public void mousePressed(MouseEvent e){
            if(SwingUtilities.isRightMouseButton(e)){
                popupMenu.show(e.getComponent(), e.getX(), e.getY());
            }
        }
    }
    /*CopyActionだけでメニュー、ツールバー、ポップアップメニュー
    すべてのコピー機能とGUIを実装*/
    class CopyAction extends AbstractAction {
        CopyAction(){
            putValue(Action.SMALL_ICON, new ImageIcon("Copy24.gif"));
            putValue(Action.NAME, "コピー");
            putValue(Action.SHORT_DESCRIPTION, "コピー");
        }
        public void actionPerformed(ActionEvent e){
            textArea.copy();
        }
    }
    /*PasteActionだけでメニュー、ツールバー、ポップアップメニュー
    すべてのペースト機能とGUIを実装*/
    class PasteAction extends AbstractAction {
        PasteAction(){
            putValue(Action.SMALL_ICON, new ImageIcon("Paste24.gif"));
            putValue(Action.NAME, "貼付け");
            putValue(Action.SHORT_DESCRIPTION, "貼付け");
        }
        public void actionPerformed(ActionEvent e){
            textArea.paste();
        }
    }
}
                    
必要なアイコン
Copy24.gif Paste24.gif

実行してみましょう。
最初のスクリーンショットは、ポップアップメニューから文字列の貼付けをしようとしているところです。予め「Javaで楽しく」がコピーされている状態です。

図 1 : 文字列の貼付け

さあ、どうなるでしょうか。

図 2 : 貼付けができた

できました。
メニュー、ツールバー、ポップアップメニューのいずれからでも、コピーとペーストが実行できます。

次の部分に注目してください。CopyAction、PasteActionが今回の主役Actionクラスです。
この部分だけで、メニューアイテム、ツールバーのボタン、ポップアップメニューアイテムのアイコン、テキスト、ツールチップ、機能がまとめて設定されているのです。これらActionクラスのインスタンスをメニューなどに追加するだけでGUIが完成します。

    /*CopyActionだけでメニュー、ツールバー、ポップアップメニュー
    すべてのコピー機能とGUIを実装*/
    class CopyAction extends AbstractAction {
        CopyAction(){
            putValue(Action.SMALL_ICON, new ImageIcon("Copy24.gif"));
            putValue(Action.NAME, "コピー");
            putValue(Action.SHORT_DESCRIPTION, "コピー");
        }
        public void actionPerformed(ActionEvent e){
            textArea.copy();
        }
    }
    /*PasteActionだけでメニュー、ツールバー、ポップアップメニュー
    すべてのペースト機能とGUIを実装*/
    class PasteAction extends AbstractAction {
        PasteAction(){
            putValue(Action.SMALL_ICON, new ImageIcon("Paste24.gif"));
            putValue(Action.NAME, "貼付け");
            putValue(Action.SHORT_DESCRIPTION, "貼付け");
        }
        public void actionPerformed(ActionEvent e){
            textArea.paste();
        }
    }
                    

さらに次の部分にも注目してください。

        //編集メニュー
        ...
        editMenu.add(copyAction);
        editMenu.add(pasteAction);
        ...
        //ツールバー
        ...
        toolBar.add(copyAction);
        toolBar.add(pasteAction);
        //ポップアップメニュー
        ...
        popupMenu.add(copyAction);
        popupMenu.add(pasteAction);
                    

JMenuItemインスタンスやJButtonインスタンスを生成してから追加しているのではなくて、Actionクラスのインスタンスをそのまま追加しているところがミソです。とても楽ですね。

メリットはまだ有ります。

図 3 : モードメニューから編集不可を選ぶ

モードメニューから編集不可を選ぶと、

図 4 : 編集不可になった

メニュー、ツールバー、ポップアップメニューのすべてにおいて、貼付けができなくなりました。実はこれもコード中のただ1カ所だけで設定されています。次の部分です。

                pasteAction.setEnabled(false);//貼付け不可
                    

このように、Actionクラスを活用すると、コードが非常に簡潔になります。
また、プログラムのメンテナンスも非常に楽になります。何しろ、メニューアイテムやボタンの外観を変えるのに、Actionクラスの中だけを書き換えればいいのですから。
NetBeansやEclipseで開発する場合でさえも、Actionクラスを使うとメンテナンスが楽になるでしょう。