29 April 2015

(Java) Swingレイアウト(ダイアログ)上の要素の配置

Java Swingライブラリを用いてダイアログ・ボックスを表示する場合の、画面デザインのメモ

FlowLayout

要素が左から右へ順に並べられ、ウインドウの端に来たら次の行に“改行”されるレイアウト。改行位置を制御することは出来ないようだ。

FlowLayout(int align, int hgap, int vgap) またはFlowLayout(int align)で設定し、alignはFlowLayout.LEFT (左寄せ), FlowLayout.RIGHT (右寄せ), FlowLayout.CENTER (中央)から選択できる。

Oracleの公式ドキュメント → 「How to Use FlowLayout

20150429-dlg-flowlayout-left.jpg

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setTitle("Swingテストプログラム");
    frame.setSize(350, 250);
    
    JPanel panel = new JPanel();
    frame.setContentPane(panel);
    panel.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 20));
    panel.add(new JTextField("Text 1"));
    panel.add(new JButton("Button 1"));
    panel.add(new JTextField("Text 2"));
    panel.add(new JButton("Button 2"));
    panel.add(new JTextField("Text 3"));
    panel.add(new JButton("Button 3"));
    
    // 実行環境OSにあったUIを適用する
    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        SwingUtilities.updateComponentTreeUI(frame);
    } catch (Exception e) {
    }
    
    frame.setVisible(true);
}

BoxLayout

ウインドウいっぱいに要素を「拡大」して(縦または横に)一列に並べるレイアウト。要素により「拡大」される優先度が違うため、次の画面キャプチャのようにテキストボックスとボタンであれば、テキストボックスがウインドウ幅に合うように拡大される。

BoxLayout(Container target, int axis)で設定し、axisにはBoxLayout.X_AXIS (横に並べる), BoxLayout.Y_AXIS (縦に並べる)などから選択できる。

Oracleの公式ドキュメント → 「How to Use BoxLayout

20150429-dlg-boxlayout-y.jpg
BoxLayout.Y_AXIS の場合

20150429-dlg-boxlayout-x.jpg
BoxLayout.X_AXIS の場合

FlowLayoutのサンプルコードで赤で示したところを、次の内容に書き換えるだけ

    panel.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 20));
        ↓
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

GridLayout

要素を格子状に配置するレイアウト。縦・横の要素数を指定するほかに、縦のみ、横のみを指定してもう一方は要素の数で自動伸縮させることもできる。

GridLayout(int rows, int cols), または GridLayout(int rows, int cols, int hgap, int vgap)で設定し、rows または cols のどちらかを「ゼロ」とすれば任意の要素数を放り込めることになる。

Oracleの公式ドキュメント → 「How to Use GridLayout

20150429-dlg-gridlayout.jpg
横3カラムのみ指定の場合 : GridLayout(0, 3);

    panel.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 20));
        ↓
    panel.setLayout(new GridLayout(0, 3, 10, 10));

GridBagLayout

要素を配置するグリッドの「サイズ指定ができる」、特殊なGridLayoutと言うべきものなのだろうか。要素は、追加順に配置されるのではなく、配置するグリッド上の座標を個別に指定できる。

Oracleの公式ドキュメント → 「How to Use GridBagLayout

20150429-dlg-gridbag.jpg

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setTitle("Swingテストプログラム");
    frame.setSize(350, 250);
    
    JPanel panel = new JPanel();
    frame.setContentPane(panel);
    panel.setLayout(new GridBagLayout());
    GridBagConstraints c = new GridBagConstraints();
    
    // グリッド 0,0 に追加する要素
    c.gridx = 0;
    c.gridy = 0;
    panel.add(new JTextField("Text 0,0"), c);
    // グリッド 1,0 に追加する要素
    c.gridx = 1;
    c.gridy = 0;
    panel.add(new JTextField("Text 1,0"), c);
    // グリッド 0,1 に追加する要素
    c.gridx = 0;
    c.gridy = 1;
    c.weightx = 1.0;    // 水平サイズの分配ウエイト
    c.weighty = 1.0;    // 垂直サイズの分配ウエイト
    c.gridwidth = 3;    // 水平3セルを結合する
    c.fill = GridBagConstraints.BOTH;   // 水平・垂直に限界まで拡大
    panel.add(new JTextField("Text 0〜2,1"), c);
    // グリッド 1,2 に追加する要素
    c.gridx = 1;
    c.gridy = 2;
    c.weightx = 0.0;    // 水平サイズの分配ウエイトをデフォルト(0.0)に戻す
    c.weighty = 0.0;    // 垂直サイズの分配ウエイトをデフォルト(0.0)に戻す
    c.gridwidth = 1;    // 水平セル結合を元に戻す
    c.fill = GridBagConstraints.NONE;   // サイズ調整をデフォルトに戻す
    panel.add(new JTextField("Text 1,2"), c);
    // グリッド 2,2 に追加する要素
    c.gridx = 2;
    c.gridy = 2;
    panel.add(new JTextField("Text 2,2"), c);
    
    // 実行環境OSにあったUIを適用する
    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        SwingUtilities.updateComponentTreeUI(frame);
    } catch (Exception e) {
    }
    
    frame.setVisible(true);

BorderLayout

領域を5分割して配置するレイアウト

Oracleの公式ドキュメント → 「How to Use BorderLayout

20150429-dlg-border.jpg

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setTitle("Swingテストプログラム");
    frame.setSize(350, 250);
    
    JPanel panel = new JPanel();
    frame.setContentPane(panel);
    panel.setLayout(new BorderLayout());
    
    panel.add(new JTextField("Page Start"), BorderLayout.PAGE_START);
    panel.add(new JTextField("Line Start"), BorderLayout.LINE_START);
    panel.add(new JTextField("Center"), BorderLayout.CENTER);
    panel.add(new JTextField("Line End"), BorderLayout.LINE_END);
    panel.add(new JTextField("page End"), BorderLayout.PAGE_END);
        
    // 実行環境OSにあったUIを適用する
    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        SwingUtilities.updateComponentTreeUI(frame);
    } catch (Exception e) {
    }
    
    frame.setVisible(true);
}

応用例 : パネルを入れ子にしてデザイン

FlowLayoutは、ウインドウを伸縮すれば要素の折り返し位置がずれてしまうし、GridLayoutは要素が画面いっぱいに拡大されて見栄えが悪い場合がある。

そんな場合に、Panelを入れ子にしてデザインを工夫することもできる

20150429-dlg-2panel.jpg

ソースコードで、赤の部分が「親パネル」、青の部分が「子パネル」の設定部分

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setTitle("Swingテストプログラム");
    frame.setSize(350, 250);
    
    JPanel panel_0 = new JPanel();
    frame.setContentPane(panel_0);
    panel_0.setLayout(new GridLayout(0, 1));
    
    JPanel panel_1 = new JPanel();
    panel_1.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10));
    panel_1.add(new JTextField("Text 1"));
    panel_1.add(new JButton("Button 1"));
    TitledBorder border_1 = new TitledBorder(new EtchedBorder(), "Border 1");
    panel_1.setBorder(border_1);
    JPanel panel_2 = new JPanel();
    panel_2.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10));
    panel_2.add(new JTextField("Text 2"));
    panel_2.add(new JButton("Button 2"));
    TitledBorder border_2 = new TitledBorder(new EtchedBorder(), "Border 2");
    panel_2.setBorder(border_2);
    
    panel_0.add(panel_1);
    panel_0.add(panel_2);
    
    // 実行環境OSにあったUIを適用する
    try {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        SwingUtilities.updateComponentTreeUI(frame);
    } catch (Exception e) {
    }
    
    frame.setVisible(true);
}

ウインドウサイズの自動調整

frame.pack();でウインドウサイズが自動調整される

20150429-dlg-2panel_pack.jpg

一つ上の例と同じソースコードで違いを着色する

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setTitle("Swingテストプログラム");
    frame.setSize(350, 250);
    
    JPanel panel_0 = new JPanel();
    frame.setContentPane(panel_0);
    
    〜 中略 〜
    
        SwingUtilities.updateComponentTreeUI(frame);
    } catch (Exception e) {
    }
    
    frame.pack();
    frame.setVisible(true);
}