全般的にswingのレイアウトについてかなり混乱しておられる印象です。
前置き(長くてスミマセン)
色々指摘できそうなところがあります。例えばJFrameのレイアウトマネージャーは普通は無効にしません。無効にしたときどうしなければいけないかはっきりコメントできませんが、自分自身で生成したコンポーネントだけでなくJFrameの内部を構成するJLayeredPaneなども含め全てのContainer(それは全てのswingのJComponentと同義)のレイアウトマネージャーをnullにし、全てのコンポーネントのレイアウトを自分自身で制御しないといけないんじゃないかなぁと思います(※)。
しかしJScrollPaneの振る舞いは主としてレイアウトマネージャーScrollPaneLayoutの働きに依存してていますので、そもそも「全てのレイアウトマネージャーをnullにしてしまうことはスクロールの制御もまた自前でやらねばならない」気がします。
panelにはボタンを座標指定で配置したいのでsetLayoutにnullを指定しています。
一般的に何がベストかというのは言いにくいですが、自分はレイアウトマネージャーを無効にするのは極力避けるのがよいと思います。もしnullにするならswingのレイアウト処理への影響がどうなるかを踏まえた上で、レイアウトマネージャーがレイアウトする範囲とアプリケーション自信が(レイアウトマネージャーの助けを借りずに)レイアウトする範囲を明確に区別して実装する必要があると思います。(ことによると)全てをレイアウトマネージャーに任せるよりもっとswingの動作を詳しく知っていないといけないかも知れません。
本件について、panelのレイアウトをnullにしたい理由はわかります。全ての子供のレイアウトを自前で決めたいからですね?
しかしながらこのpanel自身とそれより親の階層にあるコンポーネントのレイアウトはレイアウトマネージャーにまかせたほうがよいでしょう。そうでないと※に書いたことをやる羽目になり、ウィンドウシステム(WindowsなのかMacintoshなのかLinuxの何かのウィンドウマネージャーなのかなど)をガチガチに意識した実装になりかねませんし、いろいろな状況での自前でのレイアウトは大変面倒であると思います。
ご質問について
1(a). setPreferredSizeのメソッドはレイアウトマネージャーが無効の時は普通は使わない
なぜならこのメソッドは、レイアウトマネージャーに対して自身のサイズを「できればこうしてほしい」と宣言するためのものだからです。このメソッドを呼び出したからと言ってサイズは変化しません。レイアウトマネージャーがレイアウト(子供の位置やサイズ)を決めるにあたり「さて、君はどういう大きさにしてほしいんだい?」という意味でgetPreferrdSize()を呼び出します。setPreferredSizeはこの問いへの応答サイズを覚えておくためのメソッドにすぎないのです。
レイアウトマネージャーを無効にするならそのコンテナの子供のサイズは全て自前で計算しsetBoundsなどで設定すべきです。この違いをわかった上でsetBoundsとsetPreferredSize/setMinimumSize/setMaxinumSizeのどちらを使うか考えないと画面の初期化コードはカオスなことになってしまいます。
1(b). レイアウトマネージャーが無効でsetPreferredSizeメソッドを使わないでスクロールバーを付けることができませんでした。
実はJScrollPaneのviewportViewに設定するJPanelのレイアウトマネージャーが無効だとうまくいかないということはありません。原因は別のところにあります。JScrollPaneは垂直スクロールバーを画面上に出そうとはしているのです。しかし
- JScrollPaneの幅が510, それが乗っかっているJFrameの幅が500
矛盾してます。
これが適切にレイアウトしてくれれば普通は
JFrameの幅 > contentPaneの幅 >= JScrollPaneの幅 + 垂直スクロールバーの幅
となるようレイアウトマネージャーがよきに計らってくれます。しかしご質問のコードではそれを中途半端に無効にしているため思ったようなサイズにならず、結果としてJScrollPane自身のサイズがおかしくなり期待どおりに表示されなかったのだと思います。
- viewportViewの幅を100にしているのに実行結果に変化がない
JScrollPaneの仕様みたいです。viewportViewの幅がviewportの幅より小さいと無条件にviewport幅まで広げるみたいですね。こうなる根拠は(正直いうと気にしたことないので)正確にわかりませんが、スクロールバーのサムの位置・大きさやviewportViewの位置の計算などに都合が悪いからなのかなぁと想像します。
実装例
さて、失礼ながら現在のコードはツッコミどころが多いため「ここをこう直せば」といったコメントがしにくいです。ただご質問のコードは本件の話題を議論するのに充分シンプルな内容になっているため、似た画面を出す「こうしたらどうでしょう」といったコード例は書けそうに思いました。
Java
1import javax.swing.*;
2import java.awt.*;
3
4public class Page2 extends JFrame {
5 public static void main(String [] args) {
6 new Page2().setVisible(true);
7 }
8
9 Page2() {
10 super("タイトル");
11 setDefaultCloseOperation(EXIT_ON_CLOSE);
12
13 JPanel panel = new JPanel(null) { // constructorにLayoutManagerを指定できます。
14 { // 匿名クラスの初期化ブロックに書くとメソッドの操作対象を省略できる
15 // 親(JViewport)はレイアウトマネージャーを持っているので推奨サイズ制約を設定しておく必要がある。
16 // 逆にsetBoundsは意味がない。他も同様の考え方でどちらかを選択。
17 setPreferredSize(new Dimension(100, 3000));
18 }
19 };
20 JButton button = new JButton("ボタン") {
21 {
22 // 親のレイアウトマネージャーが無効なので自前でサイズを設定
23 setBounds(5, 5, 100, 100);
24 }
25 };
26 panel.add(button);
27
28 JScrollPane scrollPane = new JScrollPane(panel) {
29 {
30 // 親はレイアウトマネージャーを持っているのでサイズ制約を設定
31 setPreferredSize(new Dimension(510, 400));
32 }
33 };
34
35 add(scrollPane, BorderLayout.CENTER); // こう書けばcontentPaneへのaddと解釈してくれる
36 // Scrollpaneの下にもう一つ何か置いておきましょう。
37 add(new JLabel("JFrameさん私も表示してください"), BorderLayout.SOUTH);
38
39 // 全部自前でレイアウトしないかぎりpackは必須。
40 // これが書かれてないswingアプリは異端に見えます :-P
41 pack();
42 }
43}
捕捉:
(1) yukkuriさんが指摘しておられる点も踏まえたつもりのコードです。元のコードと大分変ってしまってます。
(2) JFrameのサイズを直接指定するのを好む向きもありますが、(子供コンポーネントのサイズ制約が適切に設定されていさえすれば)特にその必要は感じません。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/01/15 02:07
2019/01/15 02:43
2019/01/15 03:56 編集
2019/01/15 04:03
2019/01/15 04:44
2019/01/15 04:49
2019/01/15 04:54 編集
2019/01/15 05:42