- PR -

BorderLayout と FlowLayout の組み合わせで改行しても高さが増えない

1
投稿者投稿内容
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-08-27 10:56
unibon です。こんにちわ。

私は Swing(AWT) の LayoutManager の仕組みについて詳しくないのですが、
FlowLayout が改行してくれるから高さが増えて表示されてほしいのですが、
そうならないので悩んでいます
(Swing や AWT のバグというのではなく、使い方が間違っているのだと思っています)。
とくに、FlowLayout のパネルを BorderLayout の South に配置しているため、
BorderLayout の使い方の問題なのか、FlowLayout の使い方の問題なのか、
の切り分けができません。

つぎのようなサンプルでは、
BorderLayout の SOUTH の中に、FlowLayout のパネルを置き、
その FlowLayout の中に10個のテキストボックスと1個のボタンが、改行されてはいるのですが、
2行目は頭がちょっと見え隠れするだけで、ほとんど見えません(少しだけ見える)。
ちなみに、わざわざボタンを追加している理由は、
ボタンがないと2行目がまったく見えなくなるためです。

目論見としては、フレームをリサイズして小さくすると、
SOUTH のテキストボックスとボタンは改行されてすべて表示され、
CENTER はスクロールペインなので、スクロールして見ることができればいいから、
フレームから SOUTH の分を差し引いた小さな面積で表示してほしい
(SOUTH がぜんぶ見えることを優先してほしい)、
と考えています。
このような優先順位の指定はどうやってすればよいのか、
も、良く分かりません。

コード:
import java.awt.*;
import javax.swing.*;

public class MyLayoutTester {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        Container container = frame.getContentPane();
        container.setLayout(new BorderLayout());

        JPanel centerPanel = new JPanel();
        centerPanel.setBackground(Color.cyan);
        centerPanel.setPreferredSize(new Dimension(3000, 3000));
        JScrollPane scrollPane = new JScrollPane(centerPanel);
        scrollPane.setPreferredSize(new Dimension(200, 200));
        container.add(scrollPane, BorderLayout.CENTER);

        JPanel southPanel = new JPanel();
        southPanel.setBackground(Color.orange);
        southPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        for (int i = 0; i < 10; i++) {
            southPanel.add(new JTextField("" + i, 10));
        }
        southPanel.add(new JButton("button ga buttonda"));
        container.add(southPanel, BorderLayout.SOUTH);

        frame.pack();
        frame.setVisible(true);
    }
}


さくらば
大ベテラン
会議室デビュー日: 2002/11/12
投稿数: 145
投稿日時: 2003-08-28 03:27
こんにちは。さくらばです。

引用:

unibonさんの書き込み (2003-08-27 10:56) より:

BorderLayout の SOUTH の中に、FlowLayout のパネルを置き、
その FlowLayout の中に10個のテキストボックスと1個のボタンが、改行されてはいるのですが、
2行目は頭がちょっと見え隠れするだけで、ほとんど見えません(少しだけ見える)。
ちなみに、わざわざボタンを追加している理由は、
ボタンがないと2行目がまったく見えなくなるためです。

目論見としては、フレームをリサイズして小さくすると、
SOUTH のテキストボックスとボタンは改行されてすべて表示され、
CENTER はスクロールペインなので、スクロールして見ることができればいいから、
フレームから SOUTH の分を差し引いた小さな面積で表示してほしい
(SOUTH がぜんぶ見えることを優先してほしい)、
と考えています。
このような優先順位の指定はどうやってすればよいのか、
も、良く分かりません。



レイアウトマネージャがコンポーネントをどのように配置するかによってこの手の問題は
おきてしまうのです。GridBagLayout の場合は、SOUTH, NORTH など CENTER の回りに配置される
コンポーネントをそのコンポーネントの PreferredSize の大きさで配置し、残りを CENTER にす
るということをしています。

とすると unibon さんが仰られたように動作するような気もしますが、曲者なのが PreferredSize
です。Container の PreferredSize は、その Container のレイアウトマネージャが決定します。
この場合、SOUTH のコンポーネントは FlowLayout なので、FlowLayout の preferredLayoutSize
メソッドがコールされます。

下のコードは FlowLayout.java の preferredLayoutSize メソッドですが、PreferredSize は
コンポーネントを横一列に並べた時の大きさになっていることが分かります。

したがって、BorderLayout は横一列の時の高さから SOUTH の配置分を決めるので、FlowLayout
でコンポーネントが 2 行にわたったとしても 1 行分の高さ分しか配置されないのです。

コード:
    public Dimension preferredLayoutSize(Container target) {
      synchronized (target.getTreeLock()) {
	Dimension dim = new Dimension(0, 0);
	int nmembers = target.getComponentCount();
        boolean firstVisibleComponent = true;

	for (int i = 0 ; i < nmembers ; i++) {
	    Component m = target.getComponent(i);
	    if (m.visible) {
		Dimension d = m.getPreferredSize();
		dim.height = Math.max(dim.height, d.height);
                if (firstVisibleComponent) {
                    firstVisibleComponent = false;
                } else {
                    dim.width += hgap;
                }
		dim.width += d.width;
	    }
	}
	Insets insets = target.getInsets();
	dim.width += insets.left + insets.right + hgap*2;
	dim.height += insets.top + insets.bottom + vgap*2;
	return dim;
      }
    }



それではどうすればいいかというと、こんな用途には GridBagLayout を使うのがいいと思います。
ただ、GridBagLayout は結構くせがあるので、使うのは面倒くさいですね。できれば使いたくない
というのが本音です。

# レイアウトマネージャを作ってしまえというのもありかもしれませんが

unibon さんのプログラムを GridBagLayout で書き直したものを添付しておきます。

コード:
import java.awt.*;
import javax.swing.*;

public class MyLayoutTester {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        Container container = frame.getContentPane();
        GridBagLayout layout = new GridBagLayout();
        GridBagConstraints con = new GridBagConstraints();

        container.setLayout(layout);

        JPanel centerPanel = new JPanel();
        centerPanel.setBackground(Color.cyan);
        centerPanel.setPreferredSize(new Dimension(3000, 3000));
        JScrollPane scrollPane = new JScrollPane(centerPanel);
        scrollPane.setPreferredSize(new Dimension(200, 200));

        con.fill = GridBagConstraints.BOTH;
        con.gridy = 0;
        con.weightx = 1;
        con.weighty = 0.5;
        con.gridheight = GridBagConstraints.RELATIVE;
        layout.setConstraints(scrollPane, con);

        container.add(scrollPane);

        JPanel southPanel = new JPanel();
        southPanel.setBackground(Color.orange);
        southPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        for (int i = 0; i < 10; i++) {
            southPanel.add(new JTextField("" + i, 10));
        }
        southPanel.add(new JButton("button ga buttonda"));

        con.gridy = 1;
        con.weighty = 1;
        con.gridheight = GridBagConstraints.REMAINDER;
        layout.setConstraints(southPanel, con);

        container.add(southPanel);

        frame.pack();
        frame.setVisible(true);
    }
}




unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-08-28 20:25
unibon です。こんにちわ。

引用:

さくらばさんの書き込み (2003-08-28 03:27) より:
したがって、BorderLayout は横一列の時の高さから SOUTH の配置分を決めるので、FlowLayout
でコンポーネントが 2 行にわたったとしても 1 行分の高さ分しか配置されないのです。


ご解説ありがとうございます。
FlowLayout クラスの layoutContainer メソッドと、preferredLayoutSize メソッドを見比べると、
互いの計算方法が違っていますね。
これってやはり、(バグとかではなく)こういう「仕様」なんでしょうね。
#昔からあるクラスですし。

引用:

さくらばさんの書き込み (2003-08-28 03:27) より:
それではどうすればいいかというと、こんな用途には GridBagLayout を使うのがいいと思います。
ただ、GridBagLayout は結構くせがあるので、使うのは面倒くさいですね。できれば使いたくない
というのが本音です。

# レイアウトマネージャを作ってしまえというのもありかもしれませんが

unibon さんのプログラムを GridBagLayout で書き直したものを添付しておきます。


ありがとうございます。ただ、おっしゃるように GridBagLayout は難しいですね。
添付されたコードだと、下半分の部分(従来の SOUTH の部分)が詰まらないこともあり、
レイアウトマネージャを作ってしまうほうが簡単そうに思えたので、
FlowLayout を extends して改造してみます。
ただ、これだと半分ほど作り直しになってしまうみたいであり、
あまり FlowLayout との関係に固執しなくてもよいのかもしれません。
1

スキルアップ/キャリアアップ(JOB@IT)