5

GroupLayoutを使用して奇妙な動作を観察しています。JFrame内に含まれているJTextAreaがあり、JFrameから他のコンポーネントのサイズを変更してプッシュしています。奇妙なことに、JTextAreaがその上または下に何もない(ギャップもない)ようにレイアウトを再配置すると、正常に機能します。これは、テキスト領域がコンテナにどのくらいのスペースがあるかを尋ね、他のコンポーネントに関係なく、その100%を占めるかのようです。もう1つの奇妙なことは、JTextArea(JScrollPaneではない)のサイズとコンテナー内の他のコンポーネントの高さがShort.MAX_VALUEに達したときにのみ発生するように見えることです。

スクロールペインの垂直グループの最大サイズ(コンポーネントをレイアウトに追加するとき)をShort.MAX_VALUE未満の値に指定すると、問題が修正されたように見えます(値とShortの差がある限り)。 MAX_VALUEは、のすべてのコンポーネントの高さよりも大きくなっています)。例えば

.addComponent(textArea, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE - 500)

また、優先サイズを小さな正の値に設定し、GroupLayout.PREFERRED_SIZEまたはGroupLayout.DEFAULT_SIZEの代わりに設定すると、この動作もなくなるようです。例えば

.addComponent(textArea, 0, 1, Short.MAX_VALUE)

GroupLayoutのJavaチュートリアルでは、これについて何も言及されていないようで、あらゆる場所でShort.MAX_VALUEを使用する傾向があります。グーグルで答えを見つけてみましたが、この問題を検索用語で説明するのは非常に難しいことがわかりました。

バグを見つけましたか、それともGroupLayoutを理解していませんか?後者の可能性は確かに高いようです。

この例では、単純なテキスト領域を作成します。下のボタンを押して、テキストを入力します(JScrollPane内のJTextAreaのサイズを変更します)。次に、テキスト領域内をクリックして、行を追加または削除できます。いくつかの行を追加した後、再描画ボタンをクリックして(またはフレームのサイズを変更して)、奇妙な動作を確認します。

public class GroupLayoutTest {
    public GroupLayoutTest() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JFrame frame = new JFrame("GroupLayout test");
                Container panel = frame.getContentPane();

                GroupLayout layout = new GroupLayout(panel);
                panel.setLayout(layout);

                JButton addBtn = new JButton("Add Lines");
                JButton redrawBtn = new JButton("Redraw");

                final JTextArea textArea = new JTextArea();
                final JScrollPane textPane = new JScrollPane(textArea);

                layout.setHorizontalGroup(layout.createParallelGroup()
                        .addComponent(redrawBtn)
                        .addComponent(textPane)
                        .addComponent(addBtn));

                layout.setVerticalGroup(layout.createSequentialGroup()
                        .addComponent(redrawBtn)
                        .addComponent(textPane)
                        .addComponent(addBtn));

                addBtn.addActionListener(new ActionListener() {
                    int m = 0;

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        for (int i = m; m < i + 2044; ++m) {
                            textArea.append("Line " + m + "\n");
                        }

                        // redraw the frame
                        frame.validate();
                    }
                });

                redrawBtn.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        frame.validate();
                    }
                });

                frame.setPreferredSize(new Dimension(640, 480));
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static void main(String[] args) {
        new GroupLayoutTest();
    }
}
4

2 に答える 2

2

私は GroupLayout にあまり詳しくありません。これは、ドキュメントを見て考えた結果です。うまくいけば、それは役に立ちます。私の答えはちょっと長くなったので、上で要約します:

なぜこれが起こるのですか?3つのものの組み合わせ。

  1. GroupLayout は、コンポーネントの推奨サイズを尊重します。(コメントから判断して、それがあなたがそれを選んだ理由です)。
  2. あなたの垂直グループは連続しています。一度に 1 つのコンポーネントをレイアウトします - 好みのサイズ、または余裕がある場合はそれより大きくします。
  3. JScrollPane の推奨サイズは無制限です。JTextArea の優先サイズに合わせて拡大しています...優先サイズがフレームのサイズよりも大きくなると、その下のコンポーネント用のスペースがなくなります。

優先サイズを尊重するレイアウトマネージャーを、優先サイズが非常に大きいコンポーネントと組み合わせると、通常、常にこの問題が発生します。私は広範囲にテストしていませんが、FormLayoutでも発生することはわかっています(優先サイズを使用するように指示した場合)。

説明/思考プロセスのより長い試み:

これは、テキスト領域がコンテナーにコンテナー内のスペースの量を尋ね、他のコンポーネントに関係なく、その 100% を使用しているかのようです。

テキスト領域は、コンテナーにどのくらいの大きさになるかを尋ねているのではなく、反対に、どのくらいの大きさになりたいかをコンテナーに伝えています。この場合、textArea はスクロール ペインに直接含まれています。テキスト領域の推奨サイズは、テキスト領域内のテキストのサイズに合わせて大きくなります。スクロール ペインで優先サイズを設定しない限り、スクロール ペインの優先サイズは、テキスト領域の優先サイズと共に大きくなります。そこから、レイアウト マネージャーがどのようにレイアウトするかを決定します。

この場合、垂直レイアウトの SequentialGroup で GroupLayout を使用しています。これにより、最小/優先/最大サイズに基づいてコンポーネントが順番にレイアウトされます。textPane をグループの最後の項目として再配置しても、他のコンポーネントからスペースを奪うことはありません...したがって、非常に大きなサイズの場合、GroupLayout はすべてのコンポーネントが表示されても気にしないと思います。最後のコンポーネントの後にスペースが残っていないため、コンテナが拡大されるまで表示されません。小さなサイズの場合、ユーザーはフレームのサイズを調整できます...しかし、例のような大きなサイズの場合、それは実現できません。

省略とは別に、巨大なスクロール ペインと GroupLayout の組み合わせが停止するタイミングを認識していないように見えるため、スクロール ペイン バーの下部が切り取られます。ただし、@camickrが提案したように、JScrollPaneに適切なサイズを設定するだけで、これをすべて回避できます。

もう 1 つの奇妙な点は、JTextArea (JScrollPane ではない) のサイズとコンテナー内の他のコンポーネントの高さが Short.MAX_VALUE に達した場合にのみ発生するように見えることです。

各コンポーネントのサイズをログに記録すると、JScrollPane の優先サイズを指定しない限り、常に textArea のサイズよりも大きい優先サイズになることがわかります。したがって、上記のステートメントは当てはまらないと思います。 . スクロール ペインとコンテナー内の他のコンポーネントのサイズは、依然として Short.MAX_VALUE を超えており、GroupLayout で問題が発生しているように見えますが、これは事実です。

私が見た JScrollPane のポイントは、(必要に応じてスクロール バーを使用して) 含まれる小さな領域に大きな (または潜在的に大きな) 優先サイズのコンポーネントを格納することです。その後、いつでもスクロールペインを優先サイズよりも大きくすることができます...しかし、それはスクロールペインにそれがどれくらいの大きさであるべきかを伝えることから始まります。実際、適切なサイズを設定すると、スクロール バーが必要なときに JScrollPane に効果的に通知されます。たとえば、スクロール ペインの優先サイズが 400,300 の場合、含まれるコンポーネントの優先幅が 400 (または、スクロール ペインが優先サイズよりも大きくなるコンテナ内にある場合はスクロール ペインのサイズ) を超えるときはいつでも、水平スクロール バーを表示します。高さについても同様です。そうしないと、常にあなたが持っているもののサイズに成長し、スクロールバーは必要ありません.

GroupLayout のドキュメントでは、通常、他のレイアウト ビルダー ツールで使用され、通常は開発者では使用されないと述べています (まだ使用できます)。適切に機能させるために特別な最大値を使用することが日常的に必要なレイアウトではなく、別のレイアウトを使用することを検討します。私の個人的なお気に入りは、無料の BSD ライセンスのサードパーティ レイアウト マネージャーであるFormLayoutです。通常のコンポーネントに固定サイズを指定する必要はないと思いますが、スクロール ペインの場合は、優先サイズを尊重するレイアウトで優先サイズを指定する必要があります。

すべてのレイアウトで DPI を尊重し、必要に応じて拡大し、国際化 (テキストの長さが大きく異なる場合があります) で正常に動作するようにしたいので、すべてに固定サイズを指定する必要があるものは使用したくありません。ほとんどのレイアウトでは、スクロール ペインに固定の優先サイズを設定することは悪いことではないと思います。ボタン、テキストフィールド、または明らかに好ましいサイズを持つその他のコンポーネントでこれを行うと、それほど多くはありません。

FormLayout でどのように表示されるかを次に示します (ビルダーもありますが、この場合、セルの制約を使用して列を簡単に拡張しています)。

FormLayout layout = new FormLayout(
      "pref,fill:pref:grow", // cols
      "pref,3dlu,fill:pref:grow,3dlu,pref" // rows
);

JPanel panel = new JPanel(layout);

CellConstraints cc = new CellConstraints();

panel.add(redrawBtn, cc.xy(1, 1));
panel.add(textPane, cc.xyw(1, 3, 2)); // span 2 columns
panel.add(addBtn, cc.xy(1, 5));

frame.setContentPane(panel);
于 2010-12-08T23:31:24.377 に答える
1

GroupLayout の内部構造はわかりませんが、通常は、レイアウト マネージャーに操作する情報を提供するために、textArea/scrollpane コンボの優先サイズを指定する必要があります。これは、次のいずれかを使用して行われます。

final JTextArea textArea = new JTextArea(10, 30);

また

textPane.setPreferredSize( new Dimension(400, 200) );

それ以外の場合、推奨サイズは基本的にテキスト領域に追加されたすべてのテキストのサイズであると思います。

于 2010-12-08T18:50:17.413 に答える