- ベストアンサー
テキストボックス、ボタンが表示されない!初心者のためのJavaプログラム実行時の問題
- Java初心者ですが、アプレットに画像とテキストボックス、ボタンを張り付けるプログラムを書きました。
- しかし、実行すると画像を張り付けているためか、テキストボックスが指定した大きさに見えず、せいぜい1文字程度の大きさにしか表示されません。
- また、ボタンも実行後には見えない状態で、マウスでクリックすると見えるようになります。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
お答えできる範囲で回答してみます。 >今回のプログラムはSample1では画像を読み込んでいるだけで、実際の作業は、Test1で行っていると思うのですが。 この場合に、画像の書き込みと部品の張り付けとはどちらを先に行っているのでしょうか。 >画像の書き込みの方を先に行っているように思うのですが。この順序は、表示的には関係ないのでしょうか。 >今回のプログラムも実質的には、一つのクラスで作業しているような気がするのですが、 質問者さんはGUIコンポーネントの階層の生成、制約の定義(指定)、レイアウト、描画をいっしょくたにお考えのように感じます。 実際は各処理には順番があります。単純化して言うと以下のようになります。 (1)階層の生成と制約の定義はコンストラクターで(画面を構成する全コンポーネントについて)一挙に行う。 (2)その後で、さらにレイアウトの決定が(やはり全コンポーネントについて)一挙に行われる。 (3)最後に親から子供に向かって各コンポーネントの描画が行われる。 「画像の書き込み」は「書き込み」というより「スクリーンへの描画」であって(3)で行われるのです。部品の貼り付けより後で行われるということになりますね。 >A:また、何故、自動的に・・・拡張されるのでしょうか。 >B:またこのJPanelの子供にしているText1も自動的に親のJPanelの領域に拡張されます。 Aは「JAppletの中身を決めるのはcontent(getContentPane()で得られるもの)だから自動的にJAppletのサイズとcontetPaneのサイズが一致する」が理由だと思います。 ここでお詫びです! Bは間違いでした。contentはJAppletのサイズに必ず一致するようですが、それ以下の子供については必ず一致するという訳ではありません。(制約の定義によって一致することもあれば一致しないこともあります) 繰り返しになっちゃいますが、JAppletで背景イメージを描画してもそれが見えないのはJAppletがイメージを描画した直後にconentの背景が上書きされてしまうからです。contentがJAppletと同じサイズになる以上、JApplet.paintで何を書いても全てcontentに上書きされます。描画の順番は前述したように親から子供に向かって行うので、大雑把にいって以下のような順で呼び出されます。 Sample1.paint <=ここで背景イメージを描画しても・・・ JPanel.paintComponent(contentのJPanelの描画) <=この時点で完全に上書きされてしまう・・・ Text1.paintComponent ...さらにその子供達の描画メソッド 補足: なるべきややこしい話を避けようと考えたため説明が不正確かも知れません。実際はJAppletの背景イメージがcontentの背景で塗りつぶされるというのはJRootPaneやcontentPaneがデフォルトではダブルバッファありかつ不透明となっている場合といった方がより正確かもです。またJAppletでどうやっても背景を描けないと言いましたが、やろうと思えばJRootPane以下の子供を透明・ダブルバッファなしにしてやれば実現はできそうです。しかし、Text1の背景として描画した方が普通かなと思いそう回答しています。
その他の回答 (4)
- KSOH
- ベストアンサー率93% (29/31)
Sample1.paintで子供コンポーネントを描画した後で背景イメージを描画しているために イメージしか見えなくなっている・・・と書きかけてそこが本質ではないことに気づきました。 (1)背景イメージをJAppletで描画する点 Sample1.paintを直しても今度は背景イメージが見えなくなると思います。 JAppletのcontentは初期状態でJPanelですが、このJPanelは自動的に アプレットのサイズと同じサイズに拡張されると思います。 またこのJPanelの子供にしているText1も自動的に親のJPanelの領域に拡張されます。 よってJApplet.paintだけをどのように直しても期待通りにはならないと思います。 前の回答でこの点の対処法を書いてませんでした。ごめんなさい。 色々方法はあるかと思いますが、私にとってわかりやすいのは JAppletで背景を描画するのではなく、その子供のText1で描画することです。 Sample1.paint()をオーバーライドするのはやめて、 背景イメージをText1へコンストラクターを通じて渡してやり、 Text1のpaintComponentで描画したらうまくいくのではないでしょうか。 public class Sample1 ... { ... public void init() { tracker = new MediaTracker(this); img = getImage(getDocumentBase(), "../a.jpg"); tracker.addImage(img, 0); t1 = new Text1(img); getContentPane().add(t1, BorderLayout.CENTER); //実は単にadd(t1,BorderLayout.CENTER); でもよいです。 } ... } class Text1 ... { Image backgroundImage; Text1(Image img) { this.backgroundImage = img; ... //その他の部品の初期化など } public void paintComponent(Graphics g) { //イメージの実際の大きさがどうであろうととりあえずこのJPanelのサイズいっぱいに描画する g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this); } } 補足1: void Text1()というメソッドを定義されています。Sample1では t1 = new Text1(); t1.Text1(); とやっていますね。これは冗長です。あるクラスを初期化する処理はコンストラクターとして定義するのが普通です。 new するだけでコンストラクターは自動的に呼ばれます。 コンストラクターはクラス名と同じ名前で戻り値の型を記述せずに定義します。 補足2: JTextField,JButton,Boxに対してsetPreferredSizeを呼び出していますが 本来あるべきサイズより小さなサイズを指定してしまってます。 実はJTextField,JButtonなど、何か描画してくれる能力をもつJComponentの部品は 自分で適切なサイズを知っていますので、そのサイズが気に入らないときを除いて 一々自分で設定する必要はないです。またBoxもBoxに配置した子供コンポーネントのサイズ を調べてこれまた自分で適切なサイズを計算してくれますので同様に設定の必要はないです。 このあたりは参考書を調べてみるか、ぶっちゃけ自分で何も指定しないときと色々な値を設定してみた ときを比べてその結果を観察してみてください。
お礼
KSOHさん、回答有難うございました。 うまく表示されるようになりました。 何故今回は表示されたのでしょうか。 何故これまでのやり方ではうまくなかったのでしょうか。 >Sample1.paintで子供コンポーネントを描画した後で背景イメージを描画しているために イメージしか見えなくなっている・・・と書きかけてそこが本質ではないことに気づきました。 #このことは、このようなプログラムを作る時には、今後も留意しておくことなのでしょうか。 >JAppletのcontentは初期状態でJPanelですが、このJPanelは自動的に アプレットのサイズと同じサイズに拡張されると思います。 #「初期状態でJPanelです・・、この意味がよく分からないのですが、具体的にはどのような意味でしょうか。また、何故、自動的にアプレットのサイズと同じサイズに拡張されるのでしょうか。 今回このことが、どのように影響していたのでしょうか。 >またこのJPanelの子供にしているText1も自動的に親のJPanelの領域に拡張されます。 よってJApplet.paintだけをどのように直しても期待通りにはならないと思います。 #何故「ext1も自動的に親のJPanelの領域に拡張され」るのでしょうか。 ext1と親のJPanelの領域は独立しているのではないでしょうか。 別別に大きさ、背景色等も設定していると思うのですが。 具体的にはどのような意味でしょうか。 今回のプログラムはSample1では画像を読み込んでいるだけで、実際の作業は、Test1で行っていると思うのですが。 この場合に、画像の書き込みと部品の張り付けとはどちらを先に行っているのでしょうか。 画像の書き込みの方を先に行っているように思うのですが。 この順序は、表示的には関係ないのでしょうか。 色々と考えて、一つのクラスで書いてやりましたが、やはりうまく出来ませんでした、 今回のプログラムも実質的には、一つのクラスで作業しているような気がするのですが、 今回のプログラムと一つのクラスで書くのとは、やはり違うのでしょうか。 コンストラクタで画像を読み込ませたことがうまく行ったことの原因でしょうか。 以上、質問ばかりですが、宜しくお願いします。
- KSOH
- ベストアンサー率93% (29/31)
すみません、JAppletのcontentPaneのデフォルトのLayoutはBorderLayoutなので、 Sample1() { getConentPane().add(new Text1(), BorderLayout.CENTER); ... とすべきですね。
お礼
KSOHさん、回答有難う御座います。 以下のように修正してやりましたが、やはり同じ動作になります。 どこかに間違いがあるのでしょうか。 宜しくお願いします。 =============================================================== public class Sample1 extends JApplet { private static final long serialVersionUID = 1L; MediaTracker tracker ; Image img ; Text1 t1 ; public void init() { Container cnt = getContentPane( ); t1 = new Text1( ) ; t1.Text1( ) ; cnt.add( t1 , BorderLayout.CENTER ) ; tracker = new MediaTracker( this ) ; img = getImage( getDocumentBase() , "../a.jpg" ) ; tracker.addImage( img , 0 ) ; } public void start() { try { tracker.waitForAll(); } catch (InterruptedException e) { } } public void paint( Graphics g ) { super.paint(g) ; g.drawImage( img , 50 , 20 , 150 , 150 , this ) ; } } //=================================================================== class Text1 extends JPanel { private static final long serialVersionUID = 1L ; JTextField jtf ; public void Text1( ) { Box box = Box.createHorizontalBox(); jtf = new JTextField( "abcde" ) ; jtf.setForeground( Color.green ) ; jtf.setPreferredSize( new Dimension(20, 30)); box.add(jtf); JButton jbt = new JButton( "Check" ) ; jbt.setBackground( Color.red ) ; jbt.setForeground( Color.blue ) ; jbt.setPreferredSize( new Dimension(30, 40)); jbt.setFont( new Font( "SansSerif" , Font.BOLD , 20 ) ) ; box.add(jbt); setBorder(BorderFactory.createEmptyBorder(30,30,30,30)); box.setBackground( Color.yellow ) ; box.setForeground( Color.blue ) ; box.setPreferredSize( new Dimension(100, 40)); add(box); } }
- KSOH
- ベストアンサー率93% (29/31)
原因はJAppletの画面領域全体がJPanelに覆われてしまうからだと思います。 提示されたコードではLayoutManagerにnullを設定して、 自分で位置とサイズをハードコードされていますが、 実際にはJAppletが子供のJPanelを自動的にアプレットのBoundに 合わせてレイアウトしているのではないかと思います。 レイアウトマネージャーに任せずに自前でレイアウトしようとすると かえって難しいことになると思いますので、最初はややこしく感じても 自分で直接レイアウトしようとせずにswingのマナーになるべく準じた ものを作るようにしたほうがよいかと思います。 例えば次のよううな感じです。 (1)コンポーネントのサイズはsetBoundsではなく、 setMinimumSize, setPreferredSize, setMaximumSizeなどで大きさの「制約」を指定します。 (2)コンポーネントの周りに余白がほしければ setBorder(BorderFactory.createEmptyBorder(...)); などで周りに余白を付けてみる。 (3)こった画面でなければとりあえず下記の使い方が単純なものの組み合わせで考えてみる ・JPanel(BorderLayoutを使う。上、下、左、右、中央のコンポーネントを簡単に指定できる) ・Box(単純に縦横に並べる。途中に余白がほしいときGlue/Strutsなどの部品なども使う) 提示されたコードであれば例えばこんな風にします。(正確に望みのレイアウトに一致しないかもですが) class Sample1 ... { Sample1() { getContentPane().add(new Text1()); ... } } class Text1 extends JPanel { Text1() { //水平Boxを子供にして、そのBoxにJTextFieldとJButtonを置く。 Box box = Box.createHorizontalBox(); add(box); box.add(new JTextField(...)); box.add(new JButton(...)); //-->もし、JTextField/JButtonの周りの余白などが欲しければBorderを設定するなり //BoxクラスにあるGlue/Strutsといった余白を詰める部品をはさみこんだりします。 //このJPanelの周囲に30ピクセルの余白を置く。 setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30)); ...その他背景色などの設定 } }
- KSOH
- ベストアンサー率93% (29/31)
JApplet.paint()は本来JAppletの配下の全子供コンポーネント達の描画のを行う役割を持っています。 しかしご質問にあるコードではJApplet.paint()を背景イメージの描画のみを行う定義として オーバーライドしているため、子供達が描画されなくなったのです。 テキストの一部のみが描画、パネルの背景色が描画されない、クリックするまでボタンが描画されないというのは これが原因と思われます。 API Documentをみるともしオーバーライドするなら、super.paint()を呼び出すようにとの記述がありますので、それに従うと以下のような感じになります。 public void paint(Graphics g) { g.drawImage(...); // 背景イメージの描画など独自にやりたいことを記述 super.paint(g); // 基底クラスが元々行っていること(子供達の描画)をやってもらうために基底クラスのメソッドを呼び出す。 }
お礼
KSOHさん、回答有難うございます。 早速以下のように変更しました。 するとパネルの色は表示されましたが、イメージが表示されません。 ウィンドの大きさを変更すると、薄らとイメージは見えますが、 大きさを確定すると、やはりイメージは見えなくなります。 テキストボックスは指定した大きさで表示されました。 ====================================================================== public void paint( Graphics g ) { g.drawImage( img , 50 , 50 , 300 , 300 , this ) ; super.paint(g); // g.drawImage( img , 50 , 50 , 300 , 300 , this ) ; ====================================================================== 以下のように変更すると、イメージ、色は表示されますが、テキストボックス等は やはり画面をクリックしないと見えませんし、 設定した大きさでは表示されません。 どのように書き変えたらよいのでしょうか。 一つのアプレットにイメージ、テキストボックス等のコンポーネント、パネル を複数表示させる(add)場合のプログラムを書く時のルールみたいなものがあるので しょうか。 それとも他のテクニック(別のクラス等での制御)みたいのものが存在するの でしょうか。 宜しくお願い致します。 ====================================================================== public void paint( Graphics g ) { // g.drawImage( img , 50 , 50 , 300 , 300 , this ) ; super.paint(g); g.drawImage( img , 50 , 50 , 300 , 300 , this ) ;
お礼
KSOHさん、回答有難うございました。 色々なサイトを調べたり、参考書などを調べてみましたが、回答の内容も何となく理解できますが、 完全にはやはり理解できません。 もう少し、勉強をしてみます。今後とも宜しくお願いします。