- PR -

JPanelの上に同じサイズのJPanelを貼ると…

投稿者投稿内容
あぶぽん
大ベテラン
会議室デビュー日: 2005/10/20
投稿数: 205
投稿日時: 2008-07-08 18:00
いつもお世話になっております。

JPanelの上に同じサイズのJPanelを貼ると、親JPanelの表示がおかしくなります。
JPanelには同じサイズのJPanelを表示させることはできないのでしょうか?

 javax.swing.JPanel
 jre1.6.0_06 / J2ME(AGUI JSR209)
 Windows XP Professional Version 2002 SP3

具体的には、下のコードで以下の条件で、親JPanelが灰色になります。
子JPanelの幅(CHILD_WIDTH)、高さ(CHILD_HEIGHT)のどちらかを1ピクセルでも
小さくすると親JPanelは描画した色(緑)になります。

コード:
// <import省略>
// パネルの上に同じサイズのパネルを貼ると…
public class PanelOnPanelWithSameSize {
    // 親JPanelのサイズ
    private static final int PARENT_WIDTH = 640;
    private static final int PARENT_HEIGHT = 480;
    
    // 子JPanelのサイズ
    private static final int CHILD_WIDTH = 640;
    private static final int CHILD_HEIGHT = 480;
    
    // JFrameのサイズ
    private static final int FRAME_WIDTH = 640;
    private static final int FRAME_HEIGHT = 480;
    
    public static void main(String[] args) {
        // 親JPanelを生成する
        JPanel parent = new JPanel() {
            // 描画処理をカスタマイズする
            public void paintComponent(Graphics g) {
                g.setColor(Color.GREEN);
                g.fillRect(0, 0, PARENT_WIDTH, PARENT_HEIGHT);
            }
        };
        parent.setSize(new Dimension(PARENT_WIDTH, PARENT_HEIGHT));
        parent.setLocation(new Point(0, 0));
        parent.setLayout(null);
        
        // 子JPanelを生成する
        JPanel child = new JPanel() {
            // 描画処理をカスタマイズする
            public void paintComponent(Graphics g) {
                g.setColor(Color.CYAN);
                g.fillRect(20, 10, 200, 100);
            }
        };
        child.setSize(new Dimension(CHILD_WIDTH, CHILD_HEIGHT));
        child.setLocation(new Point(0, 0));
        child.setLayout(null);
        
        // 親JPanelに子JPanelを貼る
        parent.add(child);
        
        // JFrameに表示
        JFrame frame = new JFrame("パネルの上に同じサイズのパネルを貼ると…");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(null);
        // コンテントペインの大きさを設定する
        frame.setContentPane(new Container() {
            public Dimension getPreferredSize() {
                return new Dimension(FRAME_WIDTH, FRAME_HEIGHT);
            }
        });
        // JPanelを貼る
        frame.getContentPane().add(parent);
        frame.pack();
        frame.setVisible(true);
    }
}

nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2008-07-08 19:12
子のpaintComponent()の先頭にsuper.paintComponent(g);を追加してください。

paintComponent()をオーバーライドしたことで、コンポーネントの描画領域が背景で塗りつぶされなくなったということのようです。
子の方が親とサイズが同じかより大きい場合は、親側が完全に隠れるので親の描画がされずに子の描画がされるために、親の背景色である緑にならないのでしょう。

もし、子を透明なコンポーネントとしたいのであれば、setOpaque(false)としてください。
この場合、親側は子に完全に覆われるとしても透明処理のために自身の描画を行うようです。
ranco
大ベテラン
会議室デビュー日: 2007/11/02
投稿数: 112
投稿日時: 2008-07-08 20:37
いつまでたっても日本語ドキュメンテーションはめちゃくちゃ&最悪:

paintComponent()のドキュメンテーション:

英語:if you do not invoker super's implementation you must honor the opaque property, that is if this component is opaque, you must completely fill in the background in a non-opaque color.

日本語: 上位オブジェクトの実装を行わない場合は、不透明プロパティーに注意する必要があります。つまりこのコンポーネントが不透明な場合は、バックグラウンドには不透明でない色を使用する必要があります。

英語には、super.paintComponent()を呼べ、呼ばないなら自分でなんとかしろ、とちゃんと書いてあるのにねー。「上位オブジェクトの実装を行わない場合は、」…どういう意味じゃ?

---
それと、何をしたいのかよく分かりませんが、こういう場合はOverlayLayoutを使ったり、あるいはJLayeredPaneを使ってください。nullレイアウトは、インタフェイスの今後のメンテナンスが収拾付かなくなるおそれ大なので、なるべく使わないように。


[ メッセージ編集済み 編集者: ranco 編集日時 2008-07-08 21:09 ]
あぶぽん
大ベテラン
会議室デビュー日: 2005/10/20
投稿数: 205
投稿日時: 2008-07-08 21:50
お返事が遅くなり失礼いたしました。

まずは、追加情報として画像をアップしました。

http://picasaweb.google.co.jp/abupon/kIiGPE
あぶぽん
大ベテラン
会議室デビュー日: 2005/10/20
投稿数: 205
投稿日時: 2008-07-08 22:02
nagiseさん、いつも有難うございます。

説明不足で申し訳ございません。
画像を見ていただけますでしょうか。

成功する場合と、失敗する場合の違いはJPanelのサイズのみです。

引用:

子のpaintComponent()の先頭にsuper.paintComponent(g);を追加してください。


やってみましたが駄目でした。結果変わらずです。

引用:

子の方が親とサイズが同じかより大きい場合は、親側が完全に隠れるので親の描画が
されずに子の描画がされるために、親の背景色である緑にならないのでしょう。


画面を見て頂ければ…子のほうが塗りつぶしている領域は小さいです。

引用:

もし、子を透明なコンポーネントとしたいのであれば、setOpaque(false)として
ください。


JPanelの場合はsetOpaque(false)は必要ないようです。
(JavaDocにデフォルト値と書かれていますし、実際そうでした)
あぶぽん
大ベテラン
会議室デビュー日: 2005/10/20
投稿数: 205
投稿日時: 2008-07-08 22:28
rancoさん、お返事有難うございます。

引用:

paintComponent()のドキュメンテーション:


調査した結果、
paintComponent()とpaint()の違いは、子コンポーネントが描画されるかどうか
ということでした。

引用:

それと、何をしたいのかよく分かりませんが、こういう場合はOverlayLayoutを
使ったり、あるいはJLayeredPaneを使ってください。nullレイアウトは、
インタフェイスの今後のメンテナンスが収拾付かなくなるおそれ大なので、
なるべく使わないように。


J2MEと書きましたが、組込みです。

今回は仕様として「絶対レイアウト」が必須となります。
ぴあちゃん
ぬし
会議室デビュー日: 2008/02/07
投稿数: 287
投稿日時: 2008-07-09 01:03
child1,child2 を用意して、child1 は緑一色、child2 は水色■
でとりあえず、重ね合わせて表示できましたよ。
登録順序っぽいです。
※パネルは都合3つになるので全部色変更してやってみました。

第一子〜第N-1子まで 全面描画してない子を登録。
最後の子に親と同じ大きさで背景を描画したものを追加。

最初の子に全面描画のパネルを持ってくるとそれしか表示されない。
ってことは、子供が一杯並ぶ時は登録順序の逆で描画されているってこと。

んーなんだか描画速度向上のための特殊な仕掛けが入っていそうな気配ですね〜。
たぶん、親全面色塗り・第一子部分色塗り・・・を1000ネストくらいしても
初期表示に掛かる時間が提示されたコードとほとんど変わらないような気がします。
未確認ですけど。子の大きさと親の大きさが変わらないなら親書く必要無いじゃん、
な制御が入ってるんじゃないでしょか?


zOrder の設定とかもやってみたけど、これはこれで微妙な結果が出ました。
やってみればわかるのでぜひお試しを。
再描画自体が動かなかった・・・・レイアウトマネージャ切っちゃったのが
まずいんじゃないかなぁ・・・

追記:テストコード載せます。最初のコードに若干手入れたもの。
if (true) とこで切り替えて実行してください。
コード:

package test.jp;

//<import省略>
//パネルの上に同じサイズのパネルを貼ると…

import javax.swing.*;
import java.awt.*;

public class PanelOnPanelWithSameSize {
// 親JPanelのサイズ
private static final int PARENT_WIDTH = 640;
private static final int PARENT_HEIGHT = 480;

// 子JPanelのサイズ
private static final int CHILD_WIDTH = 640;
private static final int CHILD_HEIGHT = 480;

// JFrameのサイズ
private static final int FRAME_WIDTH = 640;
private static final int FRAME_HEIGHT = 480;

public static void main(String[] args) {
// 親JPanelを生成する
JPanel parent = new JPanel() {
// 描画処理をカスタマイズする
public void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.drawRect(0,0, PARENT_WIDTH-1, PARENT_HEIGHT-1);
g.setColor(Color.GREEN);
g.fillRect(1, 1, PARENT_WIDTH, PARENT_HEIGHT);
}
};
parent.setSize(new Dimension(PARENT_WIDTH, PARENT_HEIGHT));
parent.setLocation(new Point(0, 0));
parent.setLayout(null);

// 子JPanelを生成する(2)
JPanel child3 = new JPanel() {
// 描画処理をカスタマイズする
public void paintComponent(Graphics g) {
g.setColor(Color.MAGENTA);
g.drawRect(0,0, PARENT_WIDTH-1, PARENT_HEIGHT-1);
g.setColor(Color.RED);
g.fillRect(200, 100, 60, 60);
}
};
child3.setSize(new Dimension(CHILD_WIDTH, CHILD_HEIGHT));
child3.setLocation(new Point(0, 0));
child3.setLayout(null);


// 子JPanelを生成する(2)
JPanel child2 = new JPanel() {
// 描画処理をカスタマイズする
public void paintComponent(Graphics g) {
g.setColor(Color.MAGENTA);
g.drawRect(0,0, PARENT_WIDTH-1, PARENT_HEIGHT-1);
g.setColor(Color.CYAN);
g.fillRect(20, 10, 200, 100);
}
};
child2.setSize(new Dimension(CHILD_WIDTH, CHILD_HEIGHT));
child2.setLocation(new Point(0, 0));
child2.setLayout(null);


// 子JPanelを生成する(1)
JPanel child1 = new JPanel() {
// 描画処理をカスタマイズする
public void paintComponent(Graphics g) {
g.setColor(Color.RED);
g.drawRect(0,0, PARENT_WIDTH-1, PARENT_HEIGHT-1);
g.setColor(Color.BLUE);
g.fillRect(1, 1, PARENT_WIDTH, PARENT_HEIGHT);
}
};
child1.setSize(new Dimension(CHILD_WIDTH, CHILD_HEIGHT));
child1.setLocation(new Point(0, 0));
child1.setLayout(null);


if (true) {
parent.add(child3);
parent.add(child2);
parent.add(child1);

}
else {
parent.add(child1);
parent.add(child2);
parent.add(child3);

}


// JFrameに表示
JFrame frame = new JFrame("パネルの上に同じサイズのパネルを貼ると…");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);//new BorderLayout());
// コンテントペインの大きさを設定する
frame.setContentPane(new Container() {
public Dimension getPreferredSize() {
return new Dimension(FRAME_WIDTH, FRAME_HEIGHT);
}
});
// JPanelを貼る
frame.getContentPane().add(parent);//, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}



訂正2:■を追加。

[ メッセージ編集済み 編集者: ぴあちゃん 編集日時 2008-07-09 01:09 ]

[ メッセージ編集済み 編集者: ぴあちゃん 編集日時 2008-07-09 01:23 ]
ranco
大ベテラン
会議室デビュー日: 2007/11/02
投稿数: 112
投稿日時: 2008-07-09 08:52
> 親JPanelが灰色になります。
これは、子パネルのデフォルトのバックグラウンドです。JPanelはデフォルトではopaque == trueです。

> どちらかを1ピクセルでも
> 小さくすると親JPanelは描画した色(緑)になります。
Swingは、このコンポーネントの背後になにかがあるという認識でrepaint()を行い、その際、あなたのコードがopaque == trueのコンポーネントに対してopacity contractを遵守していないので、背景の塗りつぶしを行わないためです。JComponent.paintComponent()のドキュメンテーション(英文)をよく読んでください。(このドキュメンテーションも必読です:http://homepage1.nifty.com/algafield/paint.html)

そして、子が親より1ピクセルでも小さいという状態で、子のpaintComponent()の冒頭にsuper.paintComponent(g);を加えてみてください。上の私の文が、よく理解できると思います。

同サイズの状態で、子の背景を透明にするためには、現状のあなたのコードに対し、child.setSize()の直前にchild.setOpaque(false);の1行を追加するだけでOKです。



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