Swingでのレイアウトの組み方

shuji_w6e2009-05-05

GUIビルダーを使用していると、JFrameの上にラベルやボタンなどのコンポーネントをサクサクと配置し、位置を調整してレイアウトすることが出来ます。これらのやり方は小さなアプリケーションであれば問題になりませんが、ある程度の規模のGUIアプリケーションを作っていく場合は避けるべき方法です。

レイアウトの組み方

Swingでレイアウトを組む時には軽量の汎用コンテナであるJPanelを組み合わていく事が基本です。この時、あまり大きなコンポーネントとはならないように注意する事で再利用もしやすいコンポーネントとなります。その代わりにアプリケーションの機能などは上手く設計する必要があります。
考え方としてはHTML(XHTML)でWebページをコーディングする場合と非常によく似ています。次の図は右側にメニューを表示し左側にメインコンテンツを配置するようなアプリケーションのレイアウトイメージです。

外枠にAppFrameというウィンドウクラスが大枠となっており、これはHTMLではWebブラウザに相当し、ここにはボタン等の配置は行われません。その内部にはAppPanelという名前の1枚のパネルが含まれています。このAppPanel自体はHTMLではdiv#wapper等に相当し、コンポーネントを収める領域に相当します。Swingの場合、このAppPanelを1枚使ってAppFrameと分離すると、Appletなどで簡単に再利用できるなどのメリットも生まれます(AppletではAppFrameに相当する部分をAppAppletで置き換える)。AppPanelの内部にはレイアウトの構成によって幾つかのコンポーネントに分割され、必要であればそれらのコンポーネントも内部で分割され、最も上位のレイヤーにテキストフィールドやボタンなどが配置されます。
このようにJPanelを複数組み合わせてレイアウトする事は、パフォーマンス上問題があるようにも思えます。確かに単純に貼り付けていった方がコンポーネントの数は少ないのですが、レイアウトが崩れにくく、意図したレイアウトにしやすい事、再利用しやすくなる事、コードの可読性が高まる事などメリットの方が多いでしょう。

コーディング規約

コーディングに幾つかの規約を設けておく事は可読性や生産性を高める事に大きな効果があるのはご存知かと思います。GUIアプリケーション特有の命名規約としては以下を推奨します。

動作環境
  • ターゲットはJava5とし、レイアウトはSwing拡張を使用する

理由はこのエントリーを参照

コンポーネント命名規約
  • AppFrame アプリケーションのウィンドウクラス
  • AppPanel アプリケーションのメインパネル
  • XxxMenu アプリケーションのコンテナでメニューに相当するパネル
  • YyyCanvas アプリケーションのコンテナでコンポーネントを使わずに直接レンダリングするパネル(ドローツールの描画領域など)
  • ZzzPanel アプリケーションのコンテナとなるパネル

Appの部分はアプリケーション名に置き換えても良いでしょう(例:ChikaApp, ChikaFrame)。

AppPanelの作成とAppFrameへの貼り付け

それではNetbeansを使用してAppPanelを作成してみます。新規クラスとして、JPanelフォームを作成しましょう。名前はAppPanelとします。

次にAppFrameをデザインビューで開き、AppPanelを配置します。しかし、Netbeansの自動コンパイル機能はEclipseほど洗練されていない為、手動でビルドする必要があります。Shift+F11でビルドするかプロジェクトを右クリックして「生成物を削除して構築」を選択します。

このビルド作業はGUIビルダーを使用していると頻繁に行った方が良いのでショートカットを覚えておきましょう。後はAppFrameのデザインビューに対し、AppPane.javaをドラッグ&ドロップしてください。

サイズの調整

AppPanelを単純に貼り付けると、マージンが多くある状態で見栄えがよくありません。AppPanelはAppFrameいっぱいに広がっているべきですので修正します。

GUIビルダの使いやすい点はこのようなレイアウトの修正を直感的にできる点です。AppPanelのコンポーネントをクリックし、幅と高さをAppFrameの端まで引っ張りましょう。適切なところに移動すると自動的にアジャストがかかると思います。少々のマージンを取るか、端まで伸ばすかはデザイン上のポリシーかとは思いますが、基本的にはマージン(外側の余白)よりもパディング(内側の余白)を取る方がレイアウトしやすい為、いっぱいいっぱいまで広げておきます。

リファクタリング

ここでソースを確認します(レイアウト関連などは省略)。

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {
        appPanel1 = new swingsample.AppPanel();
        org.jdesktop.layout.GroupLayout appPanel1Layout = new org.jdesktop.layout.GroupLayout(appPanel1);
        appPanel1.setLayout(appPanel1Layout);
    }// </editor-fold>
    // Variables declaration - do not modify
    private swingsample.AppPanel appPanel1;
    // End of variables declaration

ドラッグ&ドロップで貼り付けたAppPanelはローカル変数として定義されており、そのインスタンスはinitComponentsで生成されています。ところが、変数名がappPanel1とちょっと微妙な名前です。変数には必ず意味のある名前を付けなくてはなりませんのでリファクタリングしましょう。ですが、GUIビルダーで生成したコンポーネントですから「Variables declaration - do not modify」とコメントされており、直接編集は出来ません。これらの変更は全てGUIビルダーを使って行います。
変数名の変更は、インスペクタパネルを使用すると簡単にできます(表示されていない場合は、ウィンドウメニューからインスペクタパネルを表示してください)。変数名を選択した後にF2を押せば簡単に変更を行えます。

この他に変数の属性などを修正したい場合は、プロパティパネルのコードタブを使用します(表示されていない場合は、ウィンドウメニューからプロパティパネルを表示してください)。例えば、変数をfinalで宣言したい場合、変数修飾子を選択して、finalを選択します。

これで変数宣言部分が以下のようなコードに修正されました。

    // Variables declaration - do not modify
    private final swingsample.AppPanel appPanel = new swingsample.AppPanel();
    // End of variables declaration

一度定義したら変更の可能性がないJLabelなどはローカル変数としてインスタンス変数にしないような事やインスタンス生成前後に特定のコードを埋め込むなど柔軟な制御が可能です。言い換えればNetbeansGUIビルダーは、簡単にポトペタでアプリケーションを作れるというよりは、Swingのコードを簡潔にリファクタリングできるツールと捕らえるべきでしょう。したがって、Swingをある程度理解しないと使い切れない部分はあります*1

*1:コードが汚いことに抵抗がないならばかまいませんが・・・