質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

Q&A

解決済

2回答

3065閲覧

swingのJPanelに付けるスクロールバーについて

rikaruto

総合スコア16

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

0グッド

0クリップ

投稿2019/01/13 16:20

こんばんは

JPanelにJScrollPaneでスクロールバーを付けてみたのですが、コードとして適切なのかがわかりません。
コンパイルも通るし、実行もできるのですが・・・
腑に落ちない点として2つあります。
1、panel.setPreferredSize
panelにはボタンを座標指定で配置したいのでsetLayoutにnullを指定しています。
でも他の関連サイトを見ていると、setPreferredSizeのメソッドはレイアウトマネージャーが無効の時は普通は使わないという風に書かれていました。なぜダメなのかが分かりませんでした。
また、レイアウトマネージャーが無効でsetPreferredSizeメソッドを使わないでスクロールバーを付けることができませんでした。

2、new Dimension(100, 3000)
Dimensionでサイズをしていしていますが、幅の100は変えても実行結果に変化がありません。自分のプログラムがおかしいのかもしれませんが、そこが少し気持ち悪く感じます。

文章が雑で申し訳ないです。
実際の開発現場でスクロールバーをパネルに付けるとしたら、どのようにコードを書くのかをご教授ください。下記が現在のプログラムコードです。

java

1import javax.swing.*; 2import java.awt.CardLayout; 3import java.awt.BorderLayout; 4import java.awt.event.*; 5import java.awt.Color; 6import java.awt.Dimension; 7 8 9public class Page2 extends JFrame{ 10 public static void main(String [] args){ 11 Page2 frame = new Page2(); 12 frame.setLayout(null); 13 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 14 frame.setBounds(0,0,500,630); 15 frame.setTitle("タイトル"); 16 frame.setVisible(true); 17 } 18 19 Page2(){ 20 JPanel panel = new JPanel(); 21 panel.setLayout(null); 22 panel.setPreferredSize(new Dimension(100, 3000)); 23 JButton button = new JButton("ボタン"); 24 button.setBounds(0,0,100,100); 25 panel.add(button); 26 27 JScrollPane scrollpane = new JScrollPane(); 28 scrollpane.setBounds(0, 0, 510, 400); 29 this.getContentPane().add(scrollpane); 30 scrollpane.setViewportView(panel); 31 } 32}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

全般的に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のレイアウトマネージャーが無効

これが適切にレイアウトしてくれれば普通は
JFrameの幅 > contentPaneの幅 >= JScrollPaneの幅 + 垂直スクロールバーの幅
となるようレイアウトマネージャーがよきに計らってくれます。しかしご質問のコードではそれを中途半端に無効にしているため思ったようなサイズにならず、結果としてJScrollPane自身のサイズがおかしくなり期待どおりに表示されなかったのだと思います。

  1. 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/14 10:37

編集2019/01/14 10:43
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rikaruto

2019/01/15 02:07

KSwordOfHasteさん、ご回答ありがとうございます。 frame側は普通はレイアウトマネージャーを無効にしないんですね。 もう一つだけ質問してもよろしいでしょうか。 panelの親はscrollPaneになるんでしょうか、それともJViewportになるんでしょうか。
KSwordOfHaste

2019/01/15 02:43

どちらが親かの意識はそれほど必要ない気がしますがボタンクリックでpanel.getParent()をデバッグプリントしてみれば簡単に確認できると思います。やってみてください。 (正直言えば即答できる自信がないです。かつては覚えていたと思うのですが、swingをかかなくなって久しいため忘れてしまいました orz)
rikaruto

2019/01/15 03:56 編集

panel.getParent()を実行するとjavax.swing.JViewportと表記されたのでJViewportが親になるんですね。 一応panelがscrollPane.getViewport()に含まれているかを調べると"含まれている"が結果として表記されました。 if (panel.getParent() == scrollPane.getViewport()) { System.out.println("含まれている"); } else { // 含まれてない System.out.println("含まれていない"); }
KSwordOfHaste

2019/01/15 04:03

よく見たら自分の回答に「親(JViewport)」と書いてありました。聞かれると自信ないのに回答文を書く途中はよく注意せずに書いてしまったものと思います。失礼しました。
rikaruto

2019/01/15 04:44

いえ、自分の質問内容がわかりにくかったです。すみませんでした! javaでgui作成って難しいですね。 KSwordOfHasteさんはswingを何かサイトや本を参考に勉強したのでしょうか。もしよろしければ教えていただけないでしょうか。
KSwordOfHaste

2019/01/15 04:49

javaとかswingとは関係なく一般にGUIというのは難しいものです。 swingは何十年前に初めて触れたという感じです。すごいおっさんなのですよ。 当時はネットもあまり発達しておらずとにかくリファレンスを読みふけり、上に書いたような実験を繰り返しだんだんわかってきたという感じでした。今ならもっと情報が豊富なのでそういうのを利用するのがいいと思います。ケチケチせずに(w;)ちょっとぐらい古くていいから本買うのがいいかもです。
KSwordOfHaste

2019/01/15 04:54 編集

余談ですがswingは古いライブラリーというのはご存じですよね?一応JavaFXが最新です。しかしOracleも手を放しちゃったし(JDK11に入ってない)JavaFXやるよりかGUIはJavaScript+HTMLを勉強した方が使う機会が多い気がします。自分はアマチュアなので自分用のスタンドアロンアプリを作る関係上Webにはそれほど興味がないのでswingやJavaFX、PythonなどでGUIを作る機会が多い(多かった)ですがマイナー感を感じております。
rikaruto

2019/01/15 05:42

中古で本を探してみます! 今、javaを勉強していて目標はweb上で動くアプリを作ることなんですけど、まずは自分のPCだけで動くアプリを作ってみたくてswingを勉強しようと思いました。JavaFXがjavaのguiで最新というのは知っていたのですが、swingの方がyoutubeの実況動画があったり、参考サイトが多い気がしてswingにしました。 何か1つswingでアプリを作成できたら、KSwordOfHasteさんがおっしゃるようにJavaScriptやHTMLを勉強したいです。
guest

0

1.について

でも他の関連サイトを見ていると、setPreferredSizeのメソッドはレイアウトマネージャーが無効の時は普通は使わないという風に書かれていました。

また、レイアウトマネージャーが無効でsetPreferredSizeメソッドを使わないでスクロールバーを付けることができませんでした。

パネルのことならレイアウトをnullにする前に実行すればいいのでは?
また、なぜスクロールバーをつけられない、という考えになるのかわかりません。

その関連サイトがわかりませんが、この理由は正直私にもわかりません。

2.について
気になったのは、

scrollpane.setViewportView(panel);

ですかね。
普通に

JScrollPane scrollpane = new JScrollPane( スクロールバーに入れたいパネルなど );

でいいと思うんですけど... どうなんでしょうか?

また、コードとして適切か、と言われると、悪い方な気がします。
基本、mainメゾッドでインスタンスの設定をしない気がします。
コンストラクタでまとめてして、そのインスタンスを使わないのなら呼び出すだけ、
使うのなら変数で保存ですかね。

class Hoge { public static void main( String[] args ) { new Hoge(); // インスタンスを使わないなら // Hoge hoge = new Hoge(); インスタンスを使うなら } Hoge() { } }

実行結果とは関係ないけど

this.getContentPane()...

this.

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JFrame.は自分自身なので要りません。

投稿2019/01/14 01:23

編集2019/01/14 01:27
yukkuri

総合スコア624

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rikaruto

2019/01/14 03:43

yukkuriさん、回答ありがとうございます。 panel.setPreferredSize(new Dimension(100, 3000)); panel.setLayout(null); という順番にしてみました。 でもちょっとわからない所があります。 Dimensionでサイズを指定していますが、幅の100という部分は適用されないのでしょうか? それとも、 JScrollPane scrollpane = new JScrollPane(); scrollpane.setBounds(0, 0, 510, 400); での510という幅で適用されているのでしょうか。
yukkuri

2019/01/14 06:58

後者だと思います。横幅を800とかにして試してはどうでしょうか。 > 幅の100という部分は適用されない 横向きのスライドバーが出てこない、という話であればこちらのサイトが参考になります。 https://www.javadrive.jp/tutorial/jscrollpane/index3.html
KSwordOfHaste

2019/01/14 11:41

「また、コードとして適切か」以降のアドバイスはとても有用だと思いました。ただ特に1の内容は勘違いされてるようでしたので自分の回答1(a)にて少し詳しく述べてみました。 2や一部のレイアウトマネージャーを無効にする前提の話など自分にも難しくreshapeメソッドをoverrideして「誰がいつサイズを設定しているか」をスタックトレースで調べたりしました。非常に泥臭い調べ方ですけどレイアウトは難しいトピックなのでそんな泥臭さも時に手っ取り早く原因を知るには約に立つことがあると思います。
yukkuri

2019/01/15 06:21

私の知識不足で間違えた情報を書いていたようですね。申し訳ありません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問