スレッドを使って

今回の目標

スレッドを使えばpaintImmediatelyを使わずに動かすことができます。動いている間もボタンが使えますから、複数の●を同時に動かせますし、green, blue ボタンも生きていますからこのようにいつでもランダムな円を描き加えることができます。

moveDisk

時間のかかる作業は別の人に任せて

プログラムは決められた順番にしたがって作業をしていきます。この一連の作業の流れをスレッドといいます。

イベントを待ってボタンを押されたらactionPerformed()を実行するというのも一つのスレッドになっています。javaのイベントのスレッドをイベントディスパッチスレッド(EDT)と言います。

このイベントディスパッチスレッド内で時間のかかる仕事を入れてしまうと次のイベントが起こっても対応できません。そこで、時間のかかる仕事をEDTとは別のスレッドでやらせようという作戦です。

今回は円を描いてそれをゆっくり動かす仕事を別のスレッドにさせて、EDTはボタンのクリックを監視する作業にもどります。

ファイル名 MoveDisk2.java

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.image.*;

public class MoveDisk2 extends JFrame implements ActionListener{
    JButton rbtn;
    JButton gbtn;
    JButton bbtn;
    MyPanel mypnl;
    JPanel  btnpnl;

    BufferedImage buffimg;
    Graphics bfg;

    //constructor
    public MoveDisk2() {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setTitle("MoveDisk");
        mypnl = new MyPanel();
        rbtn = new JButton("start");
        gbtn = new JButton("green");
        bbtn = new JButton("blue");
        btnpnl = new JPanel();
        btnpnl.setLayout(new GridLayout(1,3,0,0));
        btnpnl.add(rbtn);
        btnpnl.add(gbtn);
        btnpnl.add(bbtn);
        setLayout(new BorderLayout());
        add(mypnl, BorderLayout.CENTER);
        add(btnpnl,BorderLayout.SOUTH);
        rbtn.addActionListener(this);
        gbtn.addActionListener(this);
        bbtn.addActionListener(this);
        pack();
        setVisible(true);
        
        //BufferedImageを作るのはpaintComponentに移した

    }
    //イベント処理
    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == rbtn) {
            //mypnl.moveDisk();    //●を描くメソッド
            Thread thread = new moveDisk(mypnl,buffimg);
            thread.start();
            //mypnl.repaint();
        }
        if (e.getSource() == gbtn) {
            mypnl.drawRdm('g');
            mypnl.repaint();
        }
        if (e.getSource() == bbtn) {
            mypnl.drawRdm('b');
            mypnl.repaint();
        }
    }
    public static void main(String[] args){
        MoveDisk2 myframe = new MoveDisk2();
    }
    
    //内部クラス
    public class MyPanel extends JPanel{
       //BufferedImage buffimg;
       //Graphics bfg;
       Color bc= new Color(255,255,191);  //背景の色
       Color c = new Color(0,0,0);  //楕円の色
       boolean firsttime = true; 
       public MyPanel(){
            setBackground(new Color(255,255,191));
            setPreferredSize(new Dimension(400,400));
                //panel側で大きさを指定する
        }

       @Override
       public void paintComponent(Graphics myg){
          super.paintComponent(myg);
          if (firsttime){  //1回目の描画でBufferedImageをつくる
              buffimg = new BufferedImage(
                 getSize().width,
                 getSize().height,
                 BufferedImage.TYPE_INT_RGB);
              bfg = buffimg.createGraphics();
              bfg.setColor(bc);
              bfg.fillRect(0, 0, getSize().width, getSize().height);
              firsttime = false;  //次回はやらない
            }
          myg.drawImage(buffimg, 0, 0
              ,getSize().width, getSize().height,this);
              //getSize().widthはMyPanelのインスタンスの幅
        }
        //円を動かすメソッド→下記内部クラスに

        //ランダムに円を描くメソッド
       public void drawRdm(char rgb) {
          for(int i=0; 10>i; i++){
                nextColor(rgb);
                bfg.setColor(c);
                int x = (int)(400*Math.random());
                int y = (int)(400*Math.random());
                int h = (int)(50*Math.random()+5);
                bfg.fillOval(x-h/2,y-h/2,h,h);
            }
        }
        //色を変化させるメソッド
       public void nextColor(char rgb){
          int r=0;
          int g=0;
          int b=0;
          if (rgb=='r'){
              r = (int)(r + 256*Math.random());
            }
          if (rgb=='g'){
              g = (int)(g + 256*Math.random());
            }
          if (rgb=='b'){
              b = (int)(b + 256*Math.random());
            }
          c = new Color(r,g,b);
        }
    }// end of class MyPanel

    //円を動かすクラス
    class moveDisk extends Thread{
        MyPanel pnl;
        BufferedImage image;
        int x = 100;
        int y ;
        int d = 10;
        int dx = 10;
        
        //constructor
        public moveDisk(MyPanel pn, BufferedImage im) {
            this.pnl   = pn;
            this.image = im;
            y = (int)(image.getHeight()*Math.random());
        }
        
        @Override
        public void run() {
           Graphics thg = image.createGraphics();
           thg.setColor(Color.red);
           thg.drawString(getName(),x-90,y);
           while ( image.getWidth() > x ){
                thg.fillOval(x-d/2,y-d/2,d,d);            
                pnl.repaint();
                x += dx;
                //100ms(0.1秒)停止
                try {
                   Thread.sleep(100);
                }
                catch(InterruptedException ex) {
                   System.err.println(ex);
                }
            }//end of while
        }//end of run
    }//end of class moveDisk
}

強調部分が主要な変更です。イベント処理(actionPerformed)で、mypnl.moveDisk()の代わりにスレッドの作成(new moveDisk())実行(start)をしています。

new moveDisk(mypnl,buffimg);でbuffimgを使っているので、BufferedImage の宣言を MyPanelの中でなくMoveDisk2の最初でおこなうようにしました。

一番大きな変更はmypnl.moveDisk()メソッドを独立したクラスにしたことです。そのために最後に持っていっています。

実行すると次の様になります。

moveDisk

課題

1.

別スレッドで実行されることを確認しなさい。

ファイル名 MoveDisk2.java

もくじ

聖愛高等学校
http://www.seiai.ed.jp/
Last Modified