最初に作った課題はボタンがあり、クリックすると楕円を描きました。これは一度見えなくなったウインドウが戻ることなどで絵が消えてしまった時に描き直して、どんな時に消えるのかいろいろ試してみるためでした。
描画メソッドをpaintComponentに書くことでウインドウが戻ったときに絵も戻るようになりました。
しかし、これでもクリックなどで絵が変化する場合はこれで満足できるわけではないということを示したいと思います。
ボタンをクリックするとランダムに円をたくさん描くプログラムを作ります。
これはクリックする度に円を新しく描き直します。また、ウインドウが戻ったときにも円を新しく描き直します。
図中、左上の数字は左がpaintComponent()内を実行した回数。右がクリックした回数です。
import java.awt.*; import javax.swing.*; import java.awt.event.*; public class EventRandom extends JFrame implements ActionListener{ JButton mybtn; MyPanel mypnl; int pcct=0; //paintComponentが呼び出された回数 int clct=0; //クリックした回数 public EventRandom() { setDefaultCloseOperation(EXIT_ON_CLOSE); setTitle("イベントで変化する模様"); setLayout(new BorderLayout()); mypnl = new MyPanel(); mybtn = new JButton("draw"); add(mypnl, BorderLayout.CENTER); add(mybtn,BorderLayout.SOUTH); mybtn.addActionListener(this); mypnl.setBackground(new Color(255,255,191)); mypnl.setPreferredSize(new Dimension(400,300)); pack(); setVisible(true); } public void actionPerformed(ActionEvent e) { if (e.getSource() == mybtn) { clct++; mypnl.repaint(); } } public static void main(String[] args){ EventRandom myframe = new EventRandom(); } public class MyPanel extends JPanel{ @Override public void paintComponent(Graphics myg){ super.paintComponent(myg); pcct++; for(int i=0; 10>i; i++){ Color rc = randomColor(); myg.setColor(rc); int x = (int)(400*Math.random()); int y = (int)(300*Math.random()); int h = (int)(50*Math.random()+5); myg.fillOval(x-h/2,y-h/2,h,h); } myg.clearRect(10,4,60,20); myg.setColor(Color.black); myg.drawString(pcct+" "+clct,10,20); } public Color randomColor(){ int r=0; int g=0; int b=0; int dc = 256; g = (int)(dc*Math.random()); if (g > 255){ g = 0; } Color c = new Color(r,g,b); return c; } } }
ポイントはpaintComponentメソッドを直接呼び出さずに、repaint()メソッドを使うことです。
actionPerformedの中に、mypnl.repaint()とあります。これでMyPanelクラスのインスタンスであるmypnlの中のpaintComponent()メソッドを呼び出すことになります。
GUIでは画面を描きなおすのはプログラムからの命令でけではありません。最小化していたウィンドウが戻されたとか、ウィンドウの大きさが変更されたなどして描きなおす場合があります。プログラムから直接 paintComponent() メソッドを呼び出すと、他の再描画要請と衝突して描けなくなったり、画面が乱れてしまったりするかもしれません。そこで、repaint() メソッドを使います。このメソッドはいろいろな所からの再描画の要請を受け付けて記録しておきます。javaはそれにしたがって衝突しないように予定を立ててpaintComponent()メソッドで順番に再描画の処理をこなしていきます。
@Override や super.paintComponent() を書くことは必須ではありません。
Mypanelは最小化していたウィンドウが戻されたとか、ウィンドウの大きさが変更されたなどで再描画されるようJPanelを継承しています。JPanel は paintComponent() というメソッドを持っていて、このなかにどのように再描画するかが書いてあります。
これを継承したクラスの中に paintComponent() というメソッドを書くことで再描画する内容を独自のものに変更しているのです。この元からあった機能を別の機能に書き換えるしくみをオーバーライドといいます。
@Override はここでこの機能を使うよという宣言です。1.5から書くようになったので書く習慣のないプログラマも多くいます。書かなくても動作に支障はありません。「次にくるメソッドはオーバーライドするものである」という注釈です。メリットは次のメソッドの綴りや引数が違っているとコンパイルできないので間違いに気づきやすいということです。
オーバーライドされることにより、もともとJPanelにあった paintComponent() メソッドは実行されなくなります。これを実行したいときは super.paintComponent() と書きます。
このプログラムでは mypnl.setBackground(new Color(255,255,191)); で背景色を指定しています。super.paintComponent() と書かない場合は指定した背景色になりません。でもこれが必要ない場合や自分で背景色の面倒を見る場合には書く必要はありません。
Color randomColor() は Colorクラスのインスタンスを返すメソッドです。頭のColorでそれとわかります。最後に Color c = new Color(r,g,b); でColorのインスタンス c を作っておいて、return c; でこの値を返しています。
返された方は Color rc = randomColor(); で Colorのインスタンスであると宣言した rc にその返されたインスタンスを割り当てています。
以前使った似たようなメソッドである nextColor() は予め宣言された c という名前のColorクラスのインスタンスから色を拾ってそれを変更するメソッドでした。nextColor() は Color c とセットで使うと覚えていなければなりません。Color randomColor() は ColorがついているのでとにかくColorのインスタンスが返ってくるとすぐにわかります。他に知識がなくても使えます。このように独立性が強いメソッドがプログラムの作成を楽にします。
この色の部分はプログラムの動作を観察するためのものです。模様には不必要なものです。
clct++はクリック回数を記録します。actionPerformed()にやってきた回数です。
pcct++は画面が描き換えられた回数を記録します。paintComponent()が呼び出された回数を数えています。
paintComponentメソッドの最後の3行は文字部分の背景をクリアしてその数を書いています。
クリックする度にクリアせず、次の様に書き足していくようにはならないでしょうか。
実はpaintComponentのすぐ後に書かれているsuper.paintComponent(myg)をコメントアウト(コメントにして実行しない様にすること)をすると上記の様になります。
public void paintComponent(Graphics myg){ //super.paintComponent(myg); pcct++; for(int i=0; 10>i; i++){
superはここではMyPanelの親クラスであるJPanelのことで、そのpaintComponentの内容を先に実行するということです。
mypnl.setBackground(new Color(255,255,191)); で指定した背景色が表示されず、JFrameのもともとの色である灰色が見えています。
それでもウインドウを最小化して戻すなどして書き直されると描きためた円はなくなってしまいます。
ウインドウの大きさを変える場合も同様になります。
上記プログラムをつくって動作を確認しなさい。