| 目次| 1234567891011121314
| 資料| 予定| 課題| 宿題|

アプレットプログラミング(2)

GUI部品を使ったアプレット

本格的なアプレットプログラムを容易に作るために,画面上に配置するボタン や選択ボックス,スクロールバーなどの部品クラス群が用意されている.また, それらの部品を見栄えよく配置するための枠を作る部品クラス群も用意されて いる.この授業の中ですべてを紹介する時間はないので参考ためのプログラム 例をここで紹介しておく.

今回の授業では,ボタンを2つ使った例を紹介する.マウスのクリック・ドラッ グを繰り返すことで複数の線分を描画し,"Delete"ボタンを押すと最後の一本 を削除,"Clear"ボタンを押すとすべての描画をキャンセルするようなアプレッ トを紹介する.

線分を描く

まず,前回の最後のプログラムを参考に,複数の線分をクリック・ドラッグで 描画していくアプレットを紹介する.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
/*
  <applet code="MouseDrawApplet" width=300 height=300>
  </applet>
*/

// 線分のクラスを定義する.
class Line{
  int start_x,start_y,end_x,end_y;
  //クラス初期化のためのメソッド
  public Line(int x1,int x2,int x3,int x4){
   start_x=x1;
   start_y=x2;
   end_x=x3;
   end_y=x4;
  }
}

public class MouseDrawApplet extends Applet
    implements KeyListener, MouseListener, MouseMotionListener{
    public Line[] lines;    // Lineの配列を保持
    int lineCount;   // linesのどこまで使われているか
    boolean dragging;// マウスをドラッグ中か
    Color lineColor;    // 表示する色を保持

    public void init(){
	lines=new Line[100]; 
	lineCount=0;
	dragging=false;
	lineColor=Color.black;

	addKeyListener(this);
	addMouseListener(this);
	addMouseMotionListener(this);
    }

    public void paint(Graphics g){
	int i;
	//描画領域を白で塗りつぶす
	g.setColor(Color.white);
	g.fillRect(0,0,300,300);
	g.setColor(lineColor);

	//今までの線分を描画
	for(i=0;i< lineCount;i++){
	    g.drawLine(lines[i].start_x,lines[i].start_y,
		       lines[i].end_x,lines[i].end_y);
	}
	//Drag中であれば赤色で描画
	if(dragging){
	    g.setColor(Color.red);
	    g.drawLine(lines[i].start_x,lines[i].start_y,
		       lines[i].end_x,lines[i].end_y);
	}
    }

    //キーボート関連のイベント処理
    public void keyPressed(KeyEvent e){
      int key=e.getKeyChar();
      //System.out.println("keyPressed("+e+","+key+")");
      if(key=='q') System.exit(0);
    }
    public void keyReleased(KeyEvent e){}
    public void keyTyped(KeyEvent e){}

    //マウス関連のイベント
    public void mousePressed(MouseEvent e){
	//新たな線分を確保
	int mx=e.getX(),my=e.getY();
	//System.out.println("mousePressed("+e+","+mx+","+my+")");
	lines[lineCount]=new Line(mx,my,mx,my);
	dragging=true;
	repaint();
    }
    public void mouseReleased(MouseEvent e){
	//線分を確定
	int mx=e.getX(),my=e.getY();
	//System.out.println("mouseUp("+e+","+mx+","+my+")");
	lines[lineCount].end_x=mx;
	lines[lineCount].end_y=my;
	lineCount++;
	dragging=false;
	repaint();
    }
    public void mouseClicked(MouseEvent e){}
    public void mouseEntered(MouseEvent e){}
    public void mouseExited(MouseEvent e){}

    //マウス動作関連のイベント処理
    public void mouseDragged(MouseEvent e){
	//線分を考慮中
	int mx=e.getX(),my=e.getY();
	//System.out.println("mouseDrag("+e+","+mx+","+my+")");
	lines[lineCount].end_x=mx;
	lines[lineCount].end_y=my;
	repaint();
    }
    public void mouseMoved(MouseEvent e){}
}

ダブルバッファリング

このプログラムを実行してみるとマウスを動かすたびに画面がちらつくことが ある.これの現象を"ダブルバッファリング"と言う技法を使って解消をはかった プログラムを次に紹介する.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
/*
  <applet code="MouseDrawApplet2" width=300 height=300>
  </applet>
*/

// 線分のクラスを定義する.
class Line{
  int start_x,start_y,end_x,end_y;
  public Line(int x1,int x2,int x3,int x4){
   start_x=x1;
   start_y=x2;
   end_x=x3;
   end_y=x4;
  }
}

public class MouseDrawApplet2 extends Applet
    implements KeyListener, MouseListener, MouseMotionListener{
    public Line[] lines;    // Lineの配列を保持
    int lineCount;   // linesのどこまで使われているか
    boolean dragging;// マウスをドラッグ中か
    Color lineColor;    // 表示する色を保持

    public void init(){
	lines=new Line[10];
	lineCount=0;
	dragging=false;
	lineColor=Color.black;
	
	addKeyListener(this);
	addMouseListener(this);
	addMouseMotionListener(this);
    }
    
    Image offScreenImage;
    Graphics offScreenGraphics;
    public void update(Graphics g){
	if(offScreenImage==null){
	    offScreenImage=createImage(300,300); 
	    offScreenGraphics=offScreenImage.getGraphics(); 
	}
	paint(offScreenGraphics); 
	g.drawImage(offScreenImage,0,0,this); 
    }
   
    public void paint(Graphics g){
	int i;
	g.setColor(Color.white);
	g.fillRect(0,0,300,300);

	g.setColor(lineColor);
	for(i=0;i< lineCount;i++){
	    g.drawLine(lines[i].start_x,lines[i].start_y,
		       lines[i].end_x,lines[i].end_y);
	}
	if(dragging){
	    g.setColor(Color.red);
	    g.drawLine(lines[i].start_x,lines[i].start_y,
		       lines[i].end_x,lines[i].end_y);
	}
    }

    public void keyPressed(KeyEvent e){
      int key=e.getKeyChar();
      //System.out.println("keyPressed("+e+","+key+")");
      if(key=='q') System.exit(0);
    }
    public void keyReleased(KeyEvent e){}
    public void keyTyped(KeyEvent e){}

    public void mousePressed(MouseEvent e){
    int mx=e.getX(),my=e.getY();
    //System.out.println("mousePressed("+e+","+mx+","+my+")");
    lines[lineCount]=new Line(mx,my,mx,my);
    dragging=true;
    repaint();
    }

    public void mouseReleased(MouseEvent e){
	int mx=e.getX(),my=e.getY();
	//System.out.println("mouseUp("+e+","+mx+","+my+")");
	lines[lineCount].end_x=mx;
	lines[lineCount].end_y=my;
	lineCount++;
	dragging=false;
	repaint();
    }
    public void mouseClicked(MouseEvent e){}
    public void mouseEntered(MouseEvent e){}
    public void mouseExited(MouseEvent e){}

    public void mouseDragged(MouseEvent e){
	int mx=e.getX(),my=e.getY();
	//System.out.println("mouseDrag("+e+","+mx+","+my+")");
	lines[lineCount].end_x=mx;
	lines[lineCount].end_y=my;
	repaint();
    }
    public void mouseMoved(MouseEvent e){}
}
このプログラムでは,最初のプログラムに以下のメソッドが追加されている.
    Image offScreenImage;
    Graphics offScreenGraphics;
    public void update(Graphics g){
	if(offScreenImage==null){
	    offScreenImage=createImage(300,300); 
	    offScreenGraphics=offScreenImage.getGraphics(); 
	}
	paint(offScreenGraphics); 
	g.drawImage(offScreenImage,0,0,this); 
    }
このアプレットでは,線分を直接画面に描画するのではなく,コンピュータの 中に仮想的な画面を用意し(updateメソッド)それに対して線分の描画を行う (paintメソッド).その後仮想画面の内容をまとめて実際の画面に一度に書き 込みこと(drawImageメソッド)で,実際の画面に対する描画回数を減らしてい る.

この仮想的な画面の事をオフスクリーンイメージと呼び,GUI 部品に描画する 際に,paint メソッドの度に変化する部分が少ない場合は,別に用意したオフ スクリーンイメージ(画面外の仮想的なスクリーン)に書き込んでおいて drawImageで一度に書き込むことによって,描画コマンドを呼び出す回数を減 らすことができる.

このオフスクリーンイメージは,描画コマンドを減らすだけではなく画面のち らつきをなくすのにも使われる.MouseDrawApplet を実行させて,マウスを動 かすと,ちらつきが目立つと思う.これはpaintメソッドを実行中,たとえば バックグラウンドを表示して,線をまだ引かないうちに画面が更新されると (コンピュータ画面の更新はテレビと比べて頻繁で1秒間に60回以上),一瞬線 がない状態が生ずるからである.

このちらつきを生じさせないために,次に表示しようとする画面全体をオフス クリーンイメージで作成しておいて,drawImageで表示する方法が用いられる. これをダブルバッファリングと呼ぶ.

ボタン部品の利用

上のプログラムにボタンを追加する前にごく簡単なボタンの利用例を紹介する. このアプレットを実行すると"Start"と描かれたボタンが用意され,一度クリッ クすると表示が"Stop"にかわる.一度書き替えられると以降はボタンをクリッ クしても何も起こらない.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
/*
<applet code="ButtonTest" width=200 height=200>
</applet>
*/
public class ButtonTest extends Applet implements ActionListener {
    Button bt;

    public void init(){
	bt = new Button("Start");	//ボタンを作る
	bt.addActionListener(this);	//'bt'の動作を登録
	add(bt);			//'bt'の存在を登録
    }
    public void actionPerformed(ActionEvent ae){
	bt.setLabel("Stop");		//'bt'の動作
    }
}

完成

/*
<applet code="DrawingApplet" width=600 height=450>
</applet>
*/
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

class Line{
  public int start_x,start_y,end_x,end_y;
  public Line(int x1,int x2,int x3,int x4){
   start_x=x1;
   start_y=x2;
   end_x=x3;
   end_y=x4;
  }
}

public class DrawingApplet extends Applet
    implements KeyListener, MouseListener, MouseMotionListener, ActionListener{
    public Line[] lines;
    int lineCount;
    boolean dragging;
    Color lineColor;
    Button deleteButton,clearButton;

    public void init(){
	deleteButton=new Button("Delete");
	add(deleteButton);
	deleteButton.addActionListener(this);
	clearButton=new Button("Clear");
	add(clearButton);
	clearButton.addActionListener(this);

	lines=new Line[100];
	lineCount=0;
	dragging=false;
	lineColor=Color.black;

	addKeyListener(this);
	addMouseListener(this);
	addMouseMotionListener(this);
    }

    public void actionPerformed(ActionEvent e){
	//System.out.println(e);
	Object source=e.getSource();

        // Deleteボタンを押されたら
	if(source.equals(deleteButton)){
	    deleteLine();
	}
        // Clearボタンを押されたら
	else if(source.equals(clearButton)){
	    clearLine();
	}
    }

    //最後の一本を消去
    public void deleteLine(){
	int size;
	if( lineCount > 0){
	    lineCount--;
	    repaint();
	}
    }
    //全部の線分を消去
    public void clearLine(){
	lineCount=0;
	repaint();
    }
    
    Image offScreenImage;
    Graphics offScreenGraphics;
    public void update(Graphics g){
	if(offScreenImage==null){
	    offScreenImage=createImage(600,400);
	    offScreenGraphics=offScreenImage.getGraphics(); 
    }
    paint(offScreenGraphics); 
    g.drawImage(offScreenImage,0,0,this);
    }
    
    public void paint(Graphics g){
	int i;
	g.setColor(Color.white);
	g.fillRect(0,0,600,400);
	
	g.setColor(lineColor);
	for(i=0;i< lineCount;i++){
	    g.drawLine(lines[i].start_x,lines[i].start_y,
		       lines[i].end_x,lines[i].end_y);
	}
	if(dragging){
	    g.setColor(Color.red);
	    g.drawLine(lines[i].start_x,lines[i].start_y,
		       lines[i].end_x,lines[i].end_y);
	}
    }

    public void keyPressed(KeyEvent e){
	int key=e.getKeyChar();
	//System.out.println("keyPressed("+e+","+key+")");
	if(key=='q') System.out.println("Select Quit from Menu.");
    }

    public void keyReleased(KeyEvent e){}
    public void keyTyped(KeyEvent e){}
    
    public void mousePressed(MouseEvent e){
	int mx=e.getX(),my=e.getY();
	//System.out.println("mousePressed("+e+","+mx+","+my+")");
	lines[lineCount]=new Line(mx,my,mx,my);
	dragging=true;
	repaint();
    }
    public void mouseReleased(MouseEvent e){
	int mx=e.getX(),my=e.getY();
	//System.out.println("mouseUp("+e+","+mx+","+my+")");
	lines[lineCount].end_x=mx;
	lines[lineCount].end_y=my;
	lineCount++;
	dragging=false;
	repaint();
    }
    public void mouseClicked(MouseEvent e){}
    public void mouseEntered(MouseEvent e){}
    public void mouseExited(MouseEvent e){}

    public void mouseDragged(MouseEvent e){
	int mx=e.getX(),my=e.getY();
	//System.out.println("mouseDrag("+e+","+mx+","+my+")");
	lines[lineCount].end_x=mx;
	lines[lineCount].end_y=my;
	repaint();
    }
    public void mouseMoved(MouseEvent e){}
}

課題

  1. ButtonTest,DrawingAppletを入力し動作を確認しなさい.
  2. ButtonTestアプレットに好きなようにボタンを追加しなさい.
  3. ButtonTestアプレットにおいて"Start"と"Stop"を交互に表示するよう に変更してみなさい
  4. DrawingAppletに線分の色を変更するボタンを追加してみなさい
    [線分毎に変更しなくてもよい]

| 目次| 1234567891011121314
| 資料| 予定| 課題| 宿題|