- PR -

JComboBox の GlassPane 上でのレンダリング

投稿者投稿内容
わたなべ
大ベテラン
会議室デビュー日: 2007/12/09
投稿数: 123
お住まい・勤務地: 札幌
投稿日時: 2007-12-11 01:00
>なぜGlassPaneに配置するのでしょうか?
ContentPaneに対してビジュアル効果を与えたり、モーダルダイアログ(風)なコンポーネントを描画するテクニックの1つです。
詳しくは最初のトピックで紹介したサイトなどをご覧ください。

>nekoyamaさん
おぉ!
なるほど。なんとなくヒントが掴めました。
この方法を使えば、サンプルでは問題が解消したのですが、実際のアプリでは解消しませんでした。
他にも色々と影響しているかもしれないので、再調査してみます。

尚、repaintの件ですが、こちらは実行しているわけではなく。
実行されているかを確認する為にオーバーライドしてみたり、ソースを追ってみただけです。
問題の原因の方向性を示してくださりありがとうございました。
nekoyama
ベテラン
会議室デビュー日: 2005/03/12
投稿数: 71
投稿日時: 2007-12-11 01:46
引用:

>なぜGlassPaneに配置するのでしょうか?
ContentPaneに対してビジュアル効果を与えたり、モーダルダイアログ(風)なコンポーネントを描画するテクニックの1つです。
詳しくは最初のトピックで紹介したサイトなどをご覧ください。


知識があるかもしれませんが。
であれば、多分後々必要になる可能性があるので、KeyboardFocusManager,RepaintManager,FocusTraversalPolicyあたりも読んでおいたほうが良いでしょう。
このあたりの知識がないと、中途半端な実装になるか、想定していない潜在的なバグにはまる事になるかもしれません。

http://www.curious-creature.org/2007/08/01/blurred-background-for-dialogs-extreme-gui-makeover-2007/は
イメージにコンポーネントを描画して、そのイメージを描画するコンポーネントを用意後、それにコンポーネントを配置、glassPaneに設定することで実現できるでしょう。
その際のglassPaneの設定は先述のコードで十分です。
また、カスタムglassPane使用後は、コメントアウトしたoldGlassPaneを元に戻せばよいです。

opacityは、オーバーレイでコンポーネントを描画する場合等に特に重要です。
わたなべ
大ベテラン
会議室デビュー日: 2007/12/09
投稿数: 123
お住まい・勤務地: 札幌
投稿日時: 2007-12-14 12:41
色々と試してみましたが、サンプルではなく実際に作ったコードでは解決に至りませんでした。
他のコンポーネントが表示されている箇所ではプルダウン(JPopupMenu)が下のレイヤーに隠れてしまいます。
JComboBoxのプルダウンの描画をいじれば表示できるかもしれませんが、この手法ではPopup等が発生しない純粋な表示のみしかできないと考えるべきと結論付けました。

結局のところ、問題点はGlassPaneより下のLayerPaneにプルダウンが描画されるから、という事ですね。

Sample
コード:
        JFrame frame = new JFrame();
        frame.setSize(200, 200);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        frame.getRootPane().getLayeredPane();
                
        JPanel panel = new JPanel(new GridBagLayout());
        panel.setOpaque(false); // これで表示はされるが・・・
        JComboBox comboBox = new JComboBox(new String[]{"A", "B", "C"});
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridy = 0;
        panel.add(comboBox, gbc);
        gbc.gridy = 1;
        panel.add(new JLabel("Hello World"),gbc);
        frame.getRootPane().setGlassPane(panel);
        panel.setVisible(true);
 


ranco
大ベテラン
会議室デビュー日: 2007/11/02
投稿数: 112
投稿日時: 2007-12-14 14:16
結局のところ、わたなべさんの最初のやり方が、glass pane上にJComboBoxを載せるための標準の方法のようです:http://www.pushing-pixels.org/?p=95
071215補記:
> panel.setOpaque(false); // これで表示はされるが・・・
つまりこの方法は間違いです。


[ メッセージ編集済み 編集者: ranco 編集日時 2007-12-15 10:34 ]
nekoyama
ベテラン
会議室デビュー日: 2005/03/12
投稿数: 71
投稿日時: 2007-12-15 01:44
引用:

わたなべさんの書き込み (2007-12-14 12:41) より:
JComboBoxのプルダウンの描画をいじれば表示できるかもしれませんが、この手法ではPopup等が発生しない純粋な表示のみしかできないと考えるべきと結論付けました。


『この手法』が何を意味するか分かりませんが、GlassPane上でダイアログのように
ポップアップを表示ということであれば、実装したこともあるので可能です。
『プルダウンの描画をいじれば』というか、ウィンドウです。たしか。

個人的には、JComboBoxはSwing全体で1,2を争うぐらい拡張しにくい醜悪なコンポーネントだと思うので、知識、コスト、意欲といった制約に引っ掛からなければという前提条件がつきますが、JComboBoxを捨てて独自でコンボボックスを実装するのが一番良いと思います。
ComboBoxModelを使用しない、ポップアップを操作しやすい実装が良いと思います。
大ベテラン
会議室デビュー日: 2006/06/28
投稿数: 116
投稿日時: 2007-12-15 17:52
APIドキュメントにはGlass PaneにコンポーネントをAddした場合の動作については記述が無いですね。
この観点から行くと、バグというより仕様漏れなのでしょう。
ただ普通、GlassPaneにコンポーネントを貼り付けるということはしません。
GlassPaneは軽量コンポーネント全体の上に表示されることを期待して作られています。
JRootPaneのドキュメントを確認してみてください。
上記JRootPaneを参照すると、今回のようなことをしたい場合JLayeredPaneを使用することが分かります。
サンプルを作成してみましたので、ご参照ください。少し長いですが。
なお、JDK5より前では少し変更しないとコンパイルが通らないと思います。

コード:
import java.awt.GridLayout;
import java.text.MessageFormat;

import javax.swing.JApplet;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.WindowConstants;

public class LayeredPaneSample extends JApplet {

	private JLayeredPane layeredPane = new JLayeredPane();
	
	public void init() {
		setContentPane(layeredPane);
		layeredPane.setLayout(null);

		JPanel panel1 = new JPanel();
		panel1.setLayout(new GridLayout(5, 1));
		for (int i = 0; i < 5; i++) {
			panel1.add(new JLabel(MessageFormat.format("Line{0, number, #}: これはテストです", i)));
		}
		layeredPane.setLayer(panel1, JLayeredPane.DEFAULT_LAYER);
		layeredPane.add(panel1);
		panel1.setBounds(0, 0, 200, 100);

		JComboBox comboBox = new JComboBox(new String[]{"A", "B", "C"});
		layeredPane.setLayer(comboBox, JLayeredPane.POPUP_LAYER);
		layeredPane.add(comboBox);
		comboBox.setBounds(10, 10, 100, 103);
    }

	public static void main(String[] args) {
		final JFrame frame = new JFrame();
		JApplet applet = new LayeredPaneSample();

		applet.init();

		frame.setContentPane(applet.getContentPane());
		frame.setBounds(100, 100, 308, 199);
		frame.setVisible(true);
		frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
	}
}


修正してコードが間延びしたので投稿しなおしました。
わたなべ
大ベテラン
会議室デビュー日: 2007/12/09
投稿数: 123
お住まい・勤務地: 札幌
投稿日時: 2007-12-16 02:27
> rancoさん
情報のソースありがとうございました。

> nekoyamaさん
『この手法』=「GlassPaneにコンポーネントを描画する」です。
ソースは斜め読み程度しか出来ていませんが、拡張するにも骨が折れそうなのは解りました。
今回はコスト的に厳しいので、時間に余裕があるときにでも自作に挑戦してみるとします。

> 暁さん
想定外の使い方ですから仕様漏れではないでしょう。
今回はリンク先にある効果を使いたく、GlassPane上で描画するという前提なので、
LayeredPaneに書けばOKというのは解決策ではないのです。
http://www.curious-creature.org/2007/08/01/blurred-background-for-dialogs-extreme-gui-makeover-2007/
ranco
大ベテラン
会議室デビュー日: 2007/11/02
投稿数: 112
投稿日時: 2007-12-16 09:36
> 実際のアプリでは解消しませんでした。
あのー、ですから、nekoyama案ではなく、わたなべさんの最初のやり方が標準のようなので、しかもみなさん、Javaのバージョンと関係なく…“あり”か?…そのやり方でソリューションしているようなので、落ち着くところは、わたなべさんの現用コードにモンダイありではないか、と容疑者が絞られてきたわけです。もちろん、容疑者であり、まだ真犯人ではありませんが。

複数のJavaバージョンで試してみましたか?

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