前のページ(×描画)に対して、メニューを追加し、ブラシを表示するまでが目標です。
そのメニュー項目を選択した時にその色のブラシフレームを出現させます。メニューは、Painter(Painter.java)クラスに作ることになります。
メニューの作成方法は、ここを参照ください。
以下に追加コードを示します。
package paint; import javax.swing.*;//JApplet 、JFrame.*のパッケージ import java.awt.*;//DimensionやColor用パッケージ import java.awt.event.*;//ActionListenerやActionEvent用パッケージ //上記パネルを配置したアプレット作品用クラス public class Painter extends JApplet implements ActionListener{ PaintPanel paintPanel; //内部に使う描画対象のパネル BrushFrame brushFrame; JMenuItem itemOpen = new JMenuItem("開く"); JMenuItem itemSave = new JMenuItem("保存"); JMenuItem itemExit = new JMenuItem("終了"); JMenuItem itemOval = new JMenuItem("円"); JMenuItem itemImg = new JMenuItem("画像"); public Painter() {//コンストラクタ paintPanel = new PaintPanel();//描画対象のパネルの生成 this.setContentPane(paintPanel);//上記パネルをフレームのパネルとして設定 //メニュー設定 JMenuBar menuBar = new JMenuBar(); this.setJMenuBar(menuBar); JMenu menu1 = new JMenu("ファイル"); menuBar.add(menu1);//メニューバーにメニューを追加 menu1.add(this.itemOpen);//メニューにメニュー項目を追加 menu1.add(this.itemSave); menu1.addSeparator();//メニューのセパレータ追加 menu1.add(this.itemExit); JMenu menu2 = new JMenu("ブラシ"); menuBar.add(menu2);//メニューバーにメニューを追加 menu2.add(this.itemOval);//メニューにメニュー項目を追加 menu2.add(this.itemImg); itemOpen.addActionListener(this);//メニュークリック処理のオブジェクトをthisと指定 itemSave.addActionListener(this); itemExit.addActionListener(this); itemOval.addActionListener(this); itemImg.addActionListener(this); } public void actionPerformed(ActionEvent e){// メニュー処理 Object obj = e.getSource(); if (obj == itemOval){//円 JColorChooser chooser = new JColorChooser();//カラー選択ダイアログ生成 Color intColor = new Color(0, 0, 255); //初期の色 Color color = chooser.showDialog(this, "色を選択ください", intColor); if (color != null) { BrushFrame window = new BrushFrame(this.paintPanel, new Dimension(25, 25)); window.drawOval(color);//選択した色の楕円ブラシにする。 } } else if (obj == itemImg){//イメージ } else if (obj == itemOpen){//開く } else if (obj == itemSave){//保存 } else if (obj == itemExit){//終了 System.exit(0);//終了 } } public static void main(String []arg){ new PainterFrame();//ローカル時用に使う下記フレーム生成 } } //アプレットでなくローカルで使う時のフレーム(上記アプレットをJRootPaneにする) class PainterFrame extends JFrame { Painter painter;//上記のアプレットパネルを管理する PainterFrame(){ //コンストラクタ this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);//閉じるボタンで終了 this.painter = new Painter();//上記Painterを生成配置 this.setContentPane(painter); this.setTitle("Painter"); this.setBounds(0, 0, 300, 300);//サイズ指定 this.setVisible(true); } }
JMenuItemやJMenuは、actionPerformedのクリック処理が呼び出せるようになっています。
それでJMenuItem オブジェクトに、addActionListenerで、ActionListenerインターフェイスを実装したこのクラスを
を指定しています。
最終的にはブラシウインドウをクリックすることで、その後のドラック描画をそのブラシにします。
つまり、PaintPanelのブラシイメージを管理しているインスタンス変数brushImgは、
最後にクリックしたブラシのイメージを持つようにするということです。
このブラッシのクリックで、どうやってPaintPanelのインスタンス変数を変更すればよいでしょうか?
⇒結論的には、ブラシで、PaintPanelのインスタンス変数を持たせ、それでアクセスします。
前述の検討イメージより、ブラシ内で管理するPaintPanelのインスタンス変数名をpaintPanelと決めます。
これでBrushFrame内のクリックイベントで、paintPanelを介してoffscrImgを変更するなどが可能となります。
(これで、ドラックで使われるイメージが変ります。)
ここで重要なのは、これらインスタンス変数の設定です。
これらインスタンス変数を正しく設定しなければ使えないわけですが、どのタイミングで行うかです。
BrushFrame内のpaintPanelはコンストラクタで設定するのがよいでしょう。
なおBrushFrameには、内部にブラシイメージのパネルを配置するのですが、そのパネルクラスを
BrushPanelの名前として、
BrushFrameのインナー(内部)クラスとして定義します。
インナー(内部)クラスにはstaticを付けたものと付かない指定がありますが、
ここで使うクラスは付かないクラスです。
staticを付けないインナー(内部)クラスの利点は、外側のクラスのフィールドを利用できるということです。
つまり、staticなしインナー(内部)クラスのオブジェクトは、外側のクラスのオブジェクトが存在する場合だけ、
そのクラスのフィールドの一部として働くように利用するクラスです。
以下にそのBrushFrameのコードを示します。
package paint;
import java.awt.*;
import javax.swing.*;
import java.awt.image.BufferedImage;//メモリイメージ用
//ブラシ用フレーム
public class BrushFrame extends JFrame
{
PaintPanel paintPanel;//作品の描画パネル(PaintPanelのインスタンス)
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 BrushFrame(PaintPanel panel, Dimension size)
{
this.paintPanel = panel;//作品のパネルを管理
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
brushPanel = new BrushPanel(size);//内部
this.getContentPane().add(brushPanel);
Point pt = new Point(0,0);
javax.swing.SwingUtilities.convertPointToScreen(pt, panel);// panelのスクリーン座標取得
this.setLocation(pt.x, pt.y);//座標スクリーン座標で位置指定
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.paintPanel.brushImg = bufImg;//PaintPanelのブラシを変更
this.repaint();
}
}
コンストラクタで、内部で管理するpaintPanelを指定させます。
そして内部で使うパネルの生成や指定サイズのメモリ・イメージ生成などを行わせます。
よって、これらに必要な情報(paintPanelと初期描画イメージのサイズ)を
コンストラクタの引数で指定しできるようにしています。
なお、メモリ・イメージへの描画は、別のメソッド(drawOval)で行わせることにしました。
そうすれば、他のイメージを用意する場合、その描画用メソッドだけ用意すればよいからです。
drawOvalメソッドは楕円用ブラシのイメージを作るもので、メモリ・イメージの中央に、メモリ・イメージ矩形に内接する楕円で塗りつぶしとします。
塗りつぶしの色は、Colorの引数で指定します。また、ここで作品が描画するブラシoffscrImgを、インスタンス変数paintPanelを介して変更しています。
補足(1) setPreferredSizeによるサイズを指定して、そのサイズのパネルを使ったBrushFrameに対し、packを実行しています。
これによりBrushFrameは、中のパネルサイズに合った適切な大きさになります。このpackメソッドは、スーパークラスWindowの機能です。
補足(2) SwingUtilities.convertPointToScreen(pt, panel)で、
panel内の座標ptをスクリーン(デスクトップ)座標に変換して、ptに設定しています。
つまりptの初期値が(0,0)の場合、第2引数で指定したコンポーネントのスクリーン位置になります。
そこへsetLocationで移動しているので、このBrushFrameの位置は、panelの位置に重なります。
このBrushFrameを、メニュー操作部に追加します。
itemOvalの項目のクリック処理でBrushFrameを生成するコードを、このボタン(→
)で追加ください。
色の部分が追加コードです。
確認したらこのボタンをクリックください。→