SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Swing再入門

Swing再入門 JavaにおけるGUIのレイアウト

第1回 レイアウトを考える

LayoutManager2は使えますか?

 LayoutManagerの場合、addする際に渡せるのはString値だけでした。より高度なレイアウトを考えるならば、addする際にレイアウトのためのオブジェクトを渡して、それを元にレイアウトできるようにしたいものです。これを行えるのが、LayoutManagerを拡張したLayoutManager2です。これは、LayoutManagerのメソッドに加え、さらにいくつかのメソッドを用意する必要があります。

package jp.codezine;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;

public class SampleApp extends JFrame implements ActionListener {
    private static final long serialVersionUID = 1L;
    
    public SampleApp(){
        this.setSize(new Dimension(300,200));
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(new MyLayout());
        
        actionPerformed(null);
    }
    
    public void actionPerformed(ActionEvent e){
        int n = new Random().nextInt(200) + 100;
        JButton btn = new JButton("button [" + n + "]");
        btn.setFont(new Font("Serif",Font.PLAIN,18));
        btn.addActionListener(this);
        this.add(btn,new Integer(n));
    }
    
    public static void main(String[] args) {
        new SampleApp().setVisible(true);
    }
}

class MyLayout implements LayoutManager2 {
    private Container container;
    private ArrayList<Component> components;
    private ArrayList<Integer> widths;
    
    public MyLayout(){
        components = new ArrayList<Component>();
        widths = new ArrayList<Integer>();
    }
    
    @Override
    public void addLayoutComponent(String name, Component comp) {
        try {
            this.addLayoutComponent(
                comp,new Integer(Integer.parseInt(name)));
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void removeLayoutComponent(Component comp) {
        int n = -1;
        for (int i = 0;i < components.size();i++){
            if (components.get(i) == comp) n = i;
        }
        components.remove(comp);
        widths.remove(n);
        if (container != null) container.doLayout();
    }
    
    @Override
    public void layoutContainer(Container parent) {
        container = parent;
        int insetSize = 20;
        int h = (parent.getHeight() - insetSize * 2);
        int size = components.size() == 0 ? 1 : components.size();
        int insetH = h / size;
        int x = insetSize;
        int y = insetSize;
        for(int i = 0;i < components.size();i++){
            Component c = components.get(i);
            int w = widths.get(i);
            c.setBounds(new Rectangle(x,y,w,insetH));
            y += insetH;
        }
    }

    @Override
    public Dimension minimumLayoutSize(Container parent) {
        return new Dimension(100,100);
    }

    @Override
    public Dimension preferredLayoutSize(Container parent) {
        return parent.getPreferredSize();
    }

    @Override
    public void addLayoutComponent(
        Component comp, Object constraints) {
        components.add(comp);
        try {
            widths.add((Integer)constraints);
        } catch(ClassCastException ex){
            widths.add(0);
        }
        if (container != null) container.doLayout();
    }

    @Override
    public float getLayoutAlignmentX(Container target) {
        return 0;
    }

    @Override
    public float getLayoutAlignmentY(Container target) {
        return 0;
    }

    @Override
    public void invalidateLayout(Container target) {}

    @Override
    public Dimension maximumLayoutSize(Container target) {
        return new Dimension(target.getMaximumSize());
    }
}
LayoutManager2を使い、Integerインスタンスを渡して横幅を指定させる。
LayoutManager2を使い、Integerインスタンスを渡して横幅を指定させる。

 ここでは先ほどのMyLayoutを改良し、横幅をIntegerインスタンスで渡すようにしてみました。コンポーネントの組み込み部分を見ると、this.add(btn,new Integer(n));となっていることが分かるでしょう。LayoutManager2から追加されたメソッドを整理すると次のようになります。

  • public void addLayoutComponent(Component comp, Object constraints)
  • addの際に、StringではなくObjectを渡してレイアウトするためのメソッドを提供するものです。
  • public float getLayoutAlignmentX(Container target)
  • X方向の配置を示す値を返すものです。
  • public float getLayoutAlignmentY(Container target)
  • Y方向の配置を示す値を返すものです。
  • public void invalidateLayout(Container target)
  • レイアウトを無効にするものです。
  • public Dimension maximumLayoutSize(Container target)
  • 引数に渡されたContainerの最大サイズを返します。

 LayoutManager2の最大のポイントは、addLayoutComponentメソッドでしょう。ここでレイアウトを行わせます。ここではIntegerを渡していますが、より細かな設定を行いたいのであれば、レイアウトの数値を保持するクラスを定義して、そのインスタンスとして渡すとよいでしょう。これは、GridBagLayoutなどの利用例を思い出すとイメージしやすいのではないでしょうか。

まとめ

 今回は、コンポーネントのレイアウトについて考えてみました。Swing用に用意されたレイアウトマネージャなどはまだまだなじみがないのではないでしょうか。それ以外のコンポーネントやコンテナのイベントを使った方法や、レイアウトマネージャの自作などは、AWTでもそのまま利用できるものです。

 レイアウトマネージャによるレイアウトは、JavaのGUIを始めたときに最初に遭遇する関門といってよいでしょう。凝ったレイアウトをするのに、ついsetLayout(null)してしまうクセがついてしまっている人、多いんじゃないでしょうか。レイアウトマネージャをnullにし、すべて手作業で組み込むのは、確実ではありますが刹那的な方法です。もし、何らかの規則性が見出せるのであれば、レイアウトマネージャとして定義しておけば後々応用が利きます。

 似たようなレイアウト処理を何度もやってるな……と感じたら、一度、レイアウトの方法を考え直してみてはいかがでしょう。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
Swing再入門連載記事一覧

もっと読む

この記事の著者

掌田 津耶乃(ショウダ ツヤノ)

三文ライター&三流プログラマ。主にビギナーに向けたプログラミング関連の執筆を中心に活動している。※現在、入門ドキュメントサイト「libro」、カード型学習サイト「CARD.tuyano.com」を公開...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1626 2007/08/29 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー