カスタムダイアログ (アプレットで使う)

前のページで示したように、JDialog継承クラスを モーダルで使う場合、所有者を指定すると使い勝手が良くなります。
JFrameまたはJDialogは所有者に指定できますが、アプレットを所有者にできません。
しかもJDialogの所有者なしで使った場合、タスクバーにも表示されないので、 ブラウザの背面に隠れると操作が行い難くなります。
この場合の簡単な解決策として、ダイアログを管理する専用フレームを設ける方法があります。
つまり、直接にアプレットからダイアログを生成するのではなく、 専用フレームを介して、それを所有者として生成する訳です。 以下で、その例のアプレットを示します。

まず、上記各ボタンで生成するダイアログクラス(BrushDialog)をのコードを示します。 コンストラクタで、オーナー用のフレーム、ダイアログ表示位置指定用のコンポーネント、 ダイアログ内のパネルサイズを指定しています。
また、BrushPanelの名前でパネルを継承した 内部クラスを作って、 そこで、外側のbufImgが管理するイメージを描画しています。

package paint;
import java.awt.*;//Point、Color Graphicsなどのパッケージ
import javax.swing.*;//JPanelのパッケージ
import java.awt.event.*;//MouseMotionListenerなどのリスナーや、MouseEventなどのイベント用
import java.awt.image.BufferedImage;//編集可能なメモリイメージ用

//ブラシ用ダイアログ
class BrushDialog extends JDialog
{
	BufferedImage bufImg;//使用するメモリイメージ
	Dimension imgSize;	//上記イメージのサイズ

	//内部で使うパネル---------------------------------------
	class BrushPanel extends JPanel
	{
		public BrushPanel(Dimension size){
			imgSize = size;
			this.setPreferredSize(size); //適切なサイズを設定
			//メモリーイメージを作成
			bufImg = new BufferedImage(size.width, size.height, BufferedImage.TYPE_4BYTE_ABGR);
		}
		//描画すべきタイミングで、呼び出されるメソッド
		public void paintComponent(Graphics g){
			super.paintComponent(g);
			Graphics2D g2 = (Graphics2D)g;//これでGraphics2Dの機能も使える
			g2.drawImage(bufImg, 0, 0, this);//メモリイメージをパネルへ描画する追加
		}
	}//----インナー(内部)クラスの終端--------------------
	BrushPanel brushPanel;// 上記インナークラスを管理するインスタンス変数

	//コンストラクタ
	public BrushDialog(JFrame owner, Component panel, Dimension size){
		super(owner);
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		brushPanel = new BrushPanel(size);//内部
		this.getContentPane().add(brushPanel);
		this.pack();//サブコンポーネントの推奨サイズおよびレイアウトに合わせたサイズ変更
		this.setVisible(true);//表示する
	}

	//メモリイメージ中央に、/4サイズの楕円を描画して、PaintPanelのブラシを指定する。
	public void drawOval(Color color){
		Graphics2D g2 = (Graphics2D)bufImg.getGraphics();
		this.setTitle(Integer.toHexString(color.getRGB()));//色の16進数値へ変換し、タイトル設定
		g2.setColor(color);
		g2.fillOval(0, 0, imgSize.width, imgSize.height);
		this.repaint();
	}
}

次に上記ダイアログの所有者になるフレーム(OwnerBrushFrame)を示しします。
上記コンストラクタの第1引数で指定するのが、このOwnerBrushFrameオブジェクトです。 このOwnerBrushFrameオブジェクトは、一つしか生成できないように細工しています。
その制御は、クラス変数のframeで行っています。最初の生成時に自身のオブジェクトを frameに管理しています。 よって、frameがnullでなければ既にオブジェクトが存在するとして例外を投げて 生成できなくしています。
また、フレームを閉じる時に、frameをnullに戻しています。その処理を行うために、 フレームを閉じる時の『自動的破棄 』を行う次の命令が存在しません。
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
この代わりにWindowListenerインターフェイスを実装し、 windowClosingのオーバライドで、this.dispose();を実行し、廃棄するタイミングを 明示的に指示するコードにしています。 そして、この閉じる所でframeをnullに戻しています。
なお、this.dispose();の実行で、ウィンドウが閉じられる処理が 継続され、最終的にwindowClosedが呼び出される流れになっています。

上記ダイアログBrushDialogを生成する場合は、 必ず以下のOwnerBrushFrameフレームを介して行われるように、 BrushDialogにダイアログを生成するcreateDialogのstaticメソッドを用意します。

//ダイアログを管理するパネル
class OwnerBrushFrame extends JFrame implements WindowListener
{
	public static JFrame frame;	//ダイアログのOwnerフレーム 
	public static BrushDialog createDialog(Component component, Dimension size)
	{
		// component の位置を取得(componentのスクリーン座標で)
		Point pt = new Point(0, 0);
		javax.swing.SwingUtilities.convertPointToScreen(pt, component);
		if (frame == null){//ダイアログ管理用フレームがなければ生成
			try {
				frame = new OwnerBrushFrame();//ダイアログ管理用で、自身のフレーム生成
			}
			catch(Exception e){
				return null;
			}
			frame.setLocation(pt.x-150, pt.y);//座標スクリーン座標で位置指定
		}
		BrushDialog dialog = new BrushDialog(frame,component, size);//ダイアログ生成
		dialog.setLocation(pt.x, pt.y);//座標スクリーン座標で位置指定
		return dialog;
	}
	public OwnerBrushFrame()throws Exception//複数の生成ができないコンストラクタ
	{
		if(frame != null) throw new Exception("複数の生成は、できません");
		if(frame == null) frame = this;
		this.setTitle("Owner Brushes Frame");
		this.pack();
		this.setVisible(true);
		this.addWindowListener(this);
	}
	//Window がアクティブに変化した時、呼び出されます。
	public void windowActivated(WindowEvent e){
	}
	//dispose の呼び出し結果で、ウィンドウクローズ処理終了時に呼び出されます。
	public void windowClosed(WindowEvent e){
		this.frame = null;
	}
	//ユーザによる閉じるボタン操作などで、閉じる処理直前に呼び出されます。
	public void windowClosing(WindowEvent e){
		this.dispose();//このオブジェクトを破棄する
	}
	//Window がアクティブでなくなったときに呼び出されます。
	public void windowDeactivated(WindowEvent e){
	}
	//最小化状態から通常の状態に変更された時、呼び出されます。
	public void windowDeiconified(WindowEvent e){
	}
	//通常の状態から最小化された状態に変更された時、呼び出されます。
	public void windowIconified(WindowEvent e){
	}
	//ウィンドウが最初に可視になった時、呼び出されます。
	public void windowOpened(WindowEvent e){
	}
}

上記のOwnerBrushFrame.createDialog(位置の基準となる部品,サイズ) を呼び出せば、BrushDialogのダイアログオブジェクト生成し、戻り値で得られるわけです。
これを利用したページ先頭のアプレットコードを以下に示します。

public class OwnerFrameApplet extends JApplet implements ActionListener {
	JButton btnRed = new JButton("赤");
	JButton btnBlue = new JButton("青");
	JButton btnGreen = new JButton("緑");
	
	public OwnerFrameApplet(){//コンストラクタ
		this.setLayout(new java.awt.FlowLayout());
		this.getContentPane().add(btnRed);
		this.getContentPane().add(btnBlue);
		this.getContentPane().add(btnGreen);

		btnRed.addActionListener(this);
		btnBlue.addActionListener(this);
		btnGreen.addActionListener(this);
	}

	public void actionPerformed(ActionEvent e){
		if (e.getSource() instanceof JButton == false) return;
		JButton btn = (JButton)e.getSource();

		if (btn == this.btnRed){
			BrushDialog dialog = OwnerBrushFrame.createDialog(btn, new Dimension(25, 25));
			dialog.drawOval(Color.red);
		} else if (btn == this.btnBlue){
			BrushDialog dialog = OwnerBrushFrame.createDialog(btn, new Dimension(25, 25));
			dialog.drawOval(Color.blue);
		} else if (btn == this.btnGreen){
			BrushDialog dialog = OwnerBrushFrame.createDialog(btn, new Dimension(25, 25));
			dialog.drawOval(Color.green);
		}
	}
}

上記を確認後、このボタンをクリックください。→