今回はJavaによる簡易ドローエディタについて。
まず、機能としては
-
- 基本図形(四角・楕円・直線)の描写
- 基本色(赤・緑・青)の描写
- 右クリックによるUndo(厳密に言えば図形削除)
の三つである。
なお、プログラムリストは以下のとおり。
1 import javax.swing.*; 2 import java.awt.*; 3 import java.awt.event.*; 4 import java.util.*; 5 6 abstract class Figure { 7 protected int x,y,width,height; 8 protected Color color; 9 public Figure(int x,int y,int w,int h,Color c) { 10 this.x = x; this.y = y; 11 width = w; height = h; 12 color = c; 13 } 14 public void setSize(int w,int h) { 15 width = w; height = h; 16 } 17 public void setLocation(int x,int y) { 18 this.x = x; this.y = y; 19 } 20 abstract public void reshape(int x1,int y1,int x2,int y2); 21 abstract public void paint(Graphics g); 22 } 23 24 class LineFigure extends Figure { 25 public LineFigure(int x,int y,int w,int h,Color c) { 26 super(x,y,w,h,c); 27 } 28 public void reshape(int x1,int y1,int x2,int y2) { 29 setLocation(x1,y1); 30 setSize(x2,y2); 31 } 32 public void paint(Graphics g) { 33 g.setColor(color); 34 g.drawLine(x,y,width,height); 35 } 36 } 37 38 class CircleFigure extends Figure { 39 public CircleFigure(int x,int y,int w,int h,Color c) { 40 super(x,y,w,h,c); 41 } 42 public void reshape(int x1,int y1,int x2,int y2) { 43 int newx = Math.min(x1,x2); 44 int newy = Math.min(y1,y2); 45 int neww = Math.abs(x1 - x2); 46 int newh = Math.abs(y1 - y2); 47 setLocation(newx,newy); 48 setSize(neww,newh); 49 } 50 public void paint(Graphics g) { 51 g.setColor(color); 52 g.drawOval(x,y,width,height); 53 } 54 } 55 56 class RectangleFigure extends Figure { 57 public RectangleFigure(int x,int y,int w,int h,Color c) { 58 super(x,y,w,h,c); 59 } 60 public void reshape(int x1,int y1,int x2,int y2) { 61 int newx = Math.min(x1,x2); 62 int newy = Math.min(y1,y2); 63 int neww = Math.abs(x1 - x2); 64 int newh = Math.abs(y1 - y2); 65 setLocation(newx,newy); 66 setSize(neww,newh); 67 } 68 public void paint(Graphics g) { 69 g.setColor(color); 70 g.drawRect(x,y,width,height); 71 } 72 } 73 74 class DrawApplication { 75 protected Vector<Figure> figures; 76 protected Figure drawingFigure; 77 protected String figurelabel; 78 protected Color currentColor; 79 protected DrawPanel drawPanel; 80 public DrawApplication() { 81 figures = new Vector<Figure>(); 82 drawingFigure = null; 83 currentColor = Color.red; 84 figurelabel = "rect"; 85 } 86 public void setDrawPanel(DrawPanel c) { 87 drawPanel = c; 88 } 89 public int getNumberOfFigures() { 90 return figures.size(); 91 } 92 public Figure getFigure(int index) { 93 return (Figure)figures.elementAt(index); 94 } 95 public void createFigure(int x,int y) { 96 Figure f = null;; 97 if(figurelabel == "rect") f = new RectangleFigure(x,y,0,0,currentColor); 98 else if(figurelabel == "circ") f = new CircleFigure(x,y,0,0,currentColor); 99 else if(figurelabel == "line") f = new LineFigure(x,y,x,y,currentColor); 100 101 figures.addElement(f); 102 drawingFigure = f; 103 drawPanel.repaint(); 104 } 105 public void reshapeFigure(int x1,int y1,int x2,int y2) { 106 if (drawingFigure != null) { 107 drawingFigure.reshape(x1,y1,x2,y2); 108 drawPanel.repaint(); 109 } 110 } 111 public void changecolor(Color c){ 112 currentColor = c; 113 } 114 public void changefigure(String s){ 115 figurelabel = s; 116 } 117 public void undo(){ 118 figures.remove(figures.size()-1); 119 drawPanel.repaint(); 120 } 121 } 122 123 124 class DrawPanel extends JPanel { 125 protected DrawApplication drawApplication; 126 public DrawPanel(DrawApplication app) { 127 setBackground(Color.white); 128 drawApplication = app; 129 } 130 public void paintComponent(Graphics g) { 131 super.paintComponent(g); 132 //[すべてのFigureをpaintする] 133 for(int i=0; i < drawApplication.getNumberOfFigures();i++){ 134 Figure f = drawApplication.getFigure(i); 135 f.paint(g); 136 } 137 } 138 } 139 140 class DrawMouseListener implements MouseListener,MouseMotionListener { 141 protected DrawApplication drawApplication; 142 protected int dragStartX,dragStartY; 143 public DrawMouseListener(DrawApplication a) { 144 drawApplication = a; 145 } 146 public void mouseClicked(MouseEvent e) { 147 } 148 public void mousePressed(MouseEvent e) { 149 dragStartX = e.getX(); dragStartY = e.getY(); 150 if(SwingUtilities.isRightMouseButton(e) == true) 151 drawApplication.undo(); 152 else if(SwingUtilities.isLeftMouseButton(e) == true) 153 drawApplication.createFigure(dragStartX,dragStartY); 154 } 155 public void mouseReleased(MouseEvent e) { 156 drawApplication.reshapeFigure(dragStartX,dragStartY,e.getX(),e.getY()); 157 } 158 public void mouseEntered(MouseEvent e) { } 159 public void mouseExited(MouseEvent e) { } 160 public void mouseDragged(MouseEvent e) { 161 drawApplication.reshapeFigure(dragStartX,dragStartY,e.getX(),e.getY()); 162 } 163 public void mouseMoved(MouseEvent e) { } 164 } 165 166 class Select implements ActionListener { 167 DrawApplication a; 168 169 Select (DrawApplication ap){ 170 a = ap; 171 } 172 public void actionPerformed(ActionEvent e){ //設定の変更 173 String es = e.getActionCommand(); 174 if(es.equals("red")) a.changecolor(Color.red); 175 if(es.equals("green")) a.changecolor(Color.green); 176 if(es.equals("blue")) a.changecolor(Color.blue); 177 if(es.equals("rect")) a.changefigure("rect"); 178 if(es.equals("circ")) a.changefigure("circ"); 179 if(es.equals("line")) a.changefigure("line"); 180 181 } 182 } 183 184 185 class DrawMain { 186 public static void main(String argv[]) { 187 JFrame f = new JFrame("Draw"); 188 JPanel pc = new JPanel(); 189 JPanel pf = new JPanel(); 190 pc.setLayout(new GridLayout(1,3)); 191 pf.setLayout(new GridLayout(1,3)); 192 JButton r = new JButton("c.red"); 193 JButton g = new JButton("c.green"); 194 JButton b = new JButton("c.blue"); 195 196 JButton rect = new JButton("□"); 197 JButton circ = new JButton("○"); 198 JButton line = new JButton("─"); 199 200 r.setActionCommand("red"); 201 g.setActionCommand("green"); 202 b.setActionCommand("blue"); 203 204 rect.setActionCommand("rect"); 205 circ.setActionCommand("circ"); 206 line.setActionCommand("line"); 207 208 DrawApplication a = new DrawApplication(); 209 DrawPanel dp = new DrawPanel(a); 210 a.setDrawPanel(dp); 211 DrawMouseListener ml = new DrawMouseListener(a); 212 dp.addMouseListener(ml); 213 dp.addMouseMotionListener(ml); 214 215 pc.add(r); 216 pc.add(g); 217 pc.add(b); 218 219 pf.add(rect); 220 pf.add(circ); 221 pf.add(line); 222 223 b.addActionListener(new Select(a)); 224 g.addActionListener(new Select(a)); 225 r.addActionListener(new Select(a)); 226 227 rect.addActionListener(new Select(a)); 228 circ.addActionListener(new Select(a)); 229 line.addActionListener(new Select(a)); 230 231 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 232 f.getContentPane().add(dp,BorderLayout.CENTER); 233 f.getContentPane().add(pc,BorderLayout.SOUTH); 234 f.getContentPane().add(pf,BorderLayout.NORTH); 235 236 f.setSize(400,300); 237 f.setVisible(true); 238 } 239 }
FIgureクラス
描写した図形の情報の更新・保存をする抽象クラス。
更新・保存する情報は
x座標、y座標、幅、高さ、色
の5つ
以下、主な公開メソッド
setSize
描写した図形の幅・高さを更新・保存するメソッドsetLocation
描写した図形のx座標、y座標を更新・保存するメソッドreshape
それぞれの図形において、マウスの操作に応じて再描写するメソッドpaint
描写した図形をパネル上に表示させるメソッド
CircleFigureクラス
線を描写するクラス
抽象クラスFigureを継承する
Figureクラスのreshapeメソッド中で
図形の情報を更新し、paintメソッド中で
色の指定と図形の描写を行う。
このクラス独自の公開メソッドはない。
CircleFigureクラス
楕円を描写するクラス
抽象クラスFigureを継承する
Figureクラスのreshapeメソッド中で
図形の情報を更新し、paintメソッド中で
色の指定と図形の描写を行う。
このクラス独自の公開メソッドはない。
RectangleFigureクラス
四角形を描写するクラス
抽象クラスFigureを継承する
Figureクラスのreshapeメソッド中で
図形の情報を更新し、paintメソッド中で
色の指定と図形の描写を行う。
このクラス独自の公開メソッドはない。
DrawApplicationクラス
図形を描写する際の様々な機能を管理・実行するクラス。
主な公開フィールドとして
・Figure型のVevtor配列のインスタンスFigure
描写した図形を保存する
・Figure型のインスタンスdrawingFigure
描写する図形の情報を管理する
・String型のインスタンスfigurelabel
描写する図形の種類を判別する
・Color型のインスタンスcurrentColor
描写する図形の色を判別する
・DrawPanel型のインスタンスdrawPanel
描写するパネルを判別する
以下、主な公開メソッドである。
setDrawPanel
DrawPanelクラスのインスタンスを設定するメソッド。getNumberOfFigures
Vector配列Figuresの要素数を返すメソッド。返り値はint型。getFigure
Vector配列Figuresのi番目に格納されている図形を返すメソッド。
引数はint型、返り値はFigure型createFigure
新たな図形を生成するメソッド。
ここで選択した図形を生成する。reshapeFigure
生成した図形の再描写を行うメソッド。
マウス操作からの情報をリアルタイムで更新する。changecolor
選択した色の情報を更新するメソッド。changefigure
選択した図形の情報を更新するメソッド。
DrawPanelクラス
図形を描写するパネルを管理するクラス。
背景色の設定と図形の表示をする。
公開フィールドとして
・DrawApplication型のインスタンス
Figuresにある図形を参照する。
以下、主な公開メソッドである。
paintComponent
すべてのFigureをpaintするメソッド。
つまり描写した図形をパネル上に表示する。
DrawMouseListenerクラス
マウスの操作情報を管理するクラス
クリックやポインタの座標などの情報を他のクラスへ渡す。
公開フィールドはDrawApplication型のインスタンス
ドラッグ開始時のマウスの座標を保存するint型の変数X,Y
以下、主な公開メソッドである。
mousePressd
マウスが押されたときの動作を決定するメソッド。
クリックされた時にまずカーソルの座標を保存。
その次にそれが左右どちらのクリックかを判定。
右クリックの場合はDrawApplicationのundoを実行。
左クリックの場合はDrawApplicationのcreatefigureを実行。mouseReleased
マウスが押されなくなったときの動作を決定するメソッド。
その時点でのマウスの座標から図形を再描写する。mouseDragged
マウスがドラッグ状態のときの動作を決定するメソッド。
ドラッグ中はリアルタイムでマウスの座標から図形を再描写する。
Selectクラス
ボタンによる動作を管理・実行するクラス。
プログラムの効率化をはかるため
色選択と図形選択の実行を同一のクラスにまとめた。
主な公開フィールドはDrawApplicationのインスタンス
以下、主な公開メソッドである。
actionPerformed
ボタンが押されたときの動作を決定するメソッド。
ActionEvent変数によって条件分岐させる。
DrawMainクラス
フレーム・パネル・ボタンの設置・管理をするクラス。
mainメソッドがここに入る。
mainメソッド内での主な公開フィールドは
ウィンドウの基板のJFrame
色選択ボタン、図形選択ボタンを設置するJPanel
計6個のボタンのJButton
DrawApplicationのインスタンス
DrawPanelのインスタンス
DrawMouseListenerのインスタンス
ここでの動作は
フレームの設置
パネルの設置(色を下段、図形を上段、描写領域はその間となる)
ボタンの設置(色はR・G・Bの3つ、図形は四角・楕円・線の3つ)
素となるアプリケーションの作成し
そのアプリケーションでの動作を描写パネルに連携させ
マウス操作によってアプリケーションが起動するよう連携させる。
また、ボタン操作と実行動作を連携させる。
そして設置したフレーム・パネル及びボタンを表示させ
フレームのサイズを設定する。
実行例
赤・緑・青それぞれの
四角形・楕円・線を描写
なお、スクリーンショットでは分からないが右クリックで
アンドゥ(現在図形の削除)ができる。
色の選択については、
JColorChooserを用いれば多機能な色の選択ができたが
今回はオリジナルとして3原色を単純なボタン選択で実現した。
また今回は余裕がなかったが、パネル上に塗りつぶし領域を置き
それをクリックすれば塗りつぶされている色で描写ができる
(windows標準ペイントソフトのような)機能も擬似的に実現できる気がした。
また、グラデーションから色を選択する機能を用いれば
もっと柔軟性に富んだ色の選択ができると思われる。
図形の選択については
今回は基本図形(四角・楕円・線)の3つを実現。
しかし、どれもGraphicクラスで簡単に実現できるものだったので
あまり、手間はかからなかった。
カーソル座標と描写開始位置を少し変えれば、
ドラッグ開始点を中心とした図形の描写ができると思われる。
また、Lineメソッドを応用させて自由曲線や多角形の作成
などもできると思われる。
いずれにしろGraphicクラスによって
図形の作成は容易であることがわかった。
Undoについては
厳密に言えば、一般の''やり直し''ではなく、図形の削除である。
つまり、一度Undo操作を行えば二度とその図形は戻ってこない。
なのでスタックを用いて削除した図形を保存・復元するRedo機能を実現すれば、
より便利なものになるのではと思った。
上記の他にも図形の選択・移動・削除や
ツールバーの追加・データの保存
右クリックによるメニューの表示
など
色々と実現してみたい機能はたくさんあった。
今回の実験を通して
Javaという言語は、膨大な数のクラスのおかげで
多機能なアプリケーションの実現が可能であるということがわかった。
今回は簡易ドローエディタという入門的なプログラミングだったが
これからもっと他のソフトも作ってみたい。
それと同時に他にどんな機能を持ったクラスやメソッドがあるのか
調べてみたいと思った。