今回は、簡単な絵をウィンドウ上に描いたり、 マウスで操作ができるようなプログラムについて学びます。 グラフィックス表示を行うための クラスライブラリの使い方を紹介します。
Javaでグラフィックス表示を行うために 代表的なAWTとSwingと呼ばれる機能を使います。
Javaをはじめとする多くのグラフィックス環境では、 表示画面を細かい点の集まりとして扱います。この点のことを画素といいます。 そして、この点が画面上のどの位置かを示すために、 左上を原点 (0,0) とした座標を用います。 例えば、幅 640 、高さ 480 の大きさのウィンドウの座標は 下の絵のようになります。
数学のグラフの y 座標を上下逆にしたものだと考えることができます。
ウィンドウ上に位置を指定して図形や文字を書く場合や、 ウィンドウ上の範囲など指定する場合、 すべてこの「座標」を使います。
さっそく、ウィンドウを開くプログラムを作りましょう。 まだ、ウィンドウを開くだけで何も中身が表示されませんが、 後の例題で様々な図形や文字の表示方法を学ぶことになります。 (ファイル名: BasicWindow.java)
import java.awt.*; import javax.swing.*; public class BasicWindow { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setTitle("Basic Window"); frame.setSize(640, 480); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
このプログラムを実行してみましょう。 中身のないウィンドウが表示されましたね。
プログラムを上から順に見ていきましょう。
import java.awt.*; import javax.swing.*;
上の部分は、Javaのクラスライブラリの中から「java.awt」と 「javax.swing」というパッケージにあるクラスを、 このプログラム内でデフォルトで使うことができるようにするための記述です。
JFrame frame = new JFrame();
クラス JFrame は、ウィンドウの「枠」を表示させるためのクラスです。 JFrame のオブジェクトを生成し、 所定のメソッドを実行することによって、 画面にウィンドウを表示することができます。
frame.setTitle("Basic Window"); frame.setSize(640, 480); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true);
上のプログラムは、JFrame オブジェクト frame に所定の値を設定し、 ウィンドウを表示するためのメソッドの実行です。 上から順番に、ウィンドウのタイトルを設定し、 大きさを設定し、 ウィンドウを閉じたときにプログラム自体を終了する設定をし、 最後にウィンドウを表示しています。
上の例題のプログラムを元に、 図形を表示させることを考えてみましょう。
クラス JFrame は、ウィンドウの「枠」を表示することしかできません。 ウィンドウの上に何か表示するには、 表示するための「土台」にあたるものが必要です。 この土台のことを「コンテナ (container)」といいます。 ウィンドウ上で良く使われるコンテナは、クラス JPanel です。
この段階では、図形を書く準備はできたことになりますが、 まだ、具体的な図形は書いていません。
import java.awt.*; import javax.swing.*; public class BasicPanel { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setTitle("Basic Panel"); frame.setSize(640, 480); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(); panel.setBackground(Color.white); frame.getContentPane().add(panel); frame.setVisible(true); } }
前の例題から付け加えたのは以下の部分です。
JPanel panel = new JPanel(); panel.setBackground(Color.white); frame.getContentPane().add(panel);
図形を書くためのコンテナであるクラス JPanel のオブジェクトを生成し、 背景色を白 (Color.white) に設定しています。 次に、ウィンドウの中に panel が表示されるように、 frame に panel を加えています。
上の例題のプログラムをベースに、簡単な図形を書いてみます。 図形を書く方法の一つに、コンテナであるクラス JPanel を継承し、 図形表示機能を付け加える方法があります。 (BasicShapes.java)
import java.awt.*; import javax.swing.*; public class BasicShapes { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setTitle("Basic Shapes"); frame.setSize(640, 480); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); MyPanel panel = new MyPanel(); frame.getContentPane().add(panel); frame.setVisible(true); } } class MyPanel extends JPanel { public MyPanel() { setBackground(Color.white); } public void paintComponent(Graphics g) { super.paintComponent(g); // ウィンドウ内に描画したい図形をここに書く g.setColor(Color.red); g.fillRect(50,100,100,100); g.drawRect(200,100,100,100); g.setColor(Color.black); g.drawLine(350,100,450,200); g.setColor(Color.gray); g.fillOval(50,240,100,100); g.drawOval(200,240,100,100); g.setColor(Color.black); g.drawString("Welcome to java graphics.", 10, 20); } }
クラス MyPanel が、元のクラス JPanel を継承して定義したクラスです。 図形を表示するためには、 メソッド paintComponent をオーバライドし、 図形を書くための処理を書き加えます。 メソッド paintComponent を見てみましょう。
g.setColor(Color.red); g.fillRect(50,100,100,100); g.drawRect(200,100,100,100);
上のプログラムは、まず描画色を赤に設定し、 中身の塗りつぶされた四角形を描き、 枠だけの四角形を描いています。
g.setColor(Color.white); g.drawLine(350,100,450,200); g.setColor(Color.gray); g.fillOval(50,240,100,100); g.drawOval(200,240,100,100); g.setColor(Color.black); g.drawString("Welcome to java graphics.", 10, 20);
それぞれ、drawLine は、線を描くためのメソッド、 fillOval は、塗りつぶされた円を描くためのメソッド、 drawOval は、円を描くためのメソッド、 drawString は、文字列を描くためのメソッドです。
各メソッドの使いかたを簡単にまとめます。 詳しくは Java SE 6 APIドキュメント の クラス Graphics の説明を参照してください。
今日最後の話題はアニメーションです。 グラフィック表示を行う本格的なアニメーションを行うことを考えてみましょう。
アニメーションを行う基本的な考え方は、 1コマ1コマ絵を書き換えることです。 いままでのプログラムでは、ウィンドウに表示させたい図形は メソッド paintComponent(Graphics g) の中に書いてきました。 paintComponent(Graphics g) を定期的に実行することで、 画面を書き換えアニメーションを行うプログラムについて考えてみます。
ボールが左から右へ移動するアニメーションを考えてみます。 (SimpleAnimation.java)
import java.awt.*; import javax.swing.*; public class SimpleAnimation { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setTitle("Simple Animation"); frame.setSize(640, 480); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); MyPanel panel = new MyPanel(); frame.getContentPane().add(panel); frame.setVisible(true); } } class MyPanel extends JPanel implements Runnable { // ボールの位置 private int position; public MyPanel() { setBackground(Color.white); // 10ミリ秒ごとに画面を書き換えるためのおまじない Thread refresh = new Thread(this); refresh.start(); } public void paintComponent(Graphics g) { super.paintComponent(g); // アニメーションの1コマごとの処理をここに書く // 座標の計算 position = position + 1; // 物体の描画 g.setColor(Color.red); g.fillOval(position, 200, 50, 50); } // 10ミリ秒ごとに画面を書き換えるためのおまじない public void run() { while(true) { repaint(); try { Thread.sleep(10); } catch(Exception e) { } } } }
下線で示した部分が、定期的に画面の書き換えを行わせるために必要な記述です。 詳しい説明は省略しますが、 これは「スレッド (thread)」と呼ばれる仕組みを使い、 メソッド run() を他のメソッドの処理と並行して動かしています。 メソッド run() の中では、 10ミリ秒おきにメソッド repaint() を実行しています。 したがって、10ミリ秒おきに paintComponent(Graphics g) によって、 画面の書き換えが行われることになります。
アニメーションの速度を調節するには、 下の部分の画面の書き換えの間隔を変更します。
Thread.sleep(10);
単位はミリ秒です。
このプログラムでは、アニメーションの1コマごとの処理を メソッド paintComponent(Graphics g) に書きます。 具体的には、ボールの位置を右に1つずらして、 ボールを描画しています。 これが10ミリ秒ごとに実行されるので、 ボールが右に移動するように見えます。
お馴染みのボールが跳ね返るアニメーションを グラフィック表示してみましょう。 クラス Ball は、以前と同じ考え方で定義し、 アニメーション実行部分に組み入れています。 (BallAnimation.java)
import java.awt.*; import javax.swing.*; public class BallAnimation { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setTitle("Ball Animation"); frame.setSize(640, 480); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); MyPanel panel = new MyPanel(); frame.getContentPane().add(panel); frame.setVisible(true); } } class MyPanel extends JPanel implements Runnable { // ボールオブジェクト Ball ball; public MyPanel() { setBackground(Color.white); // ボールオブジェクトの生成 // (初期位置, 初期速度, 移動範囲の情報を与えて初期化) ball = new Ball(100,100,2,1,0,0,610,430); Thread refresh = new Thread(this); refresh.start(); } public void paintComponent(Graphics g) { super.paintComponent(g); // アニメーション1コマ分の移動処理 ball.forward(); // ボールの描画 g.setColor(Color.red); g.fillOval(ball.getX(), ball.getY(), 20, 20); } public void run() { while(true) { repaint(); try { Thread.sleep(10); } catch(Exception e) { } } } } class Ball { private int x; private int y; private int vx; private int vy; private int left; private int right; private int top; private int bottom; public Ball(int x, int y, int vx, int vy, int l, int t, int r, int b) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; right = r; left = l; top = t; bottom = b; } public void forward() { if (x + vx < left || x + vx > right) vx = -vx; if (y + vy < top || y + vy > bottom) vy = -vy; x = x + vx; y = y + vy; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
基本図形を描くだけではなく、 外部にある画像ファイルを取り込むこともできます。 下のボールの画像ファイルをダウンロードして、 実行してください。 (BallAnimation3.java)
import java.awt.*; import javax.swing.*; import javax.imageio.*; import java.io.*; public class BallAnimation3 { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setTitle("Ball Animation"); frame.setSize(640, 480); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); MyPanel panel = new MyPanel(); frame.getContentPane().add(panel); frame.setVisible(true); } } class MyPanel extends JPanel implements Runnable { private Ball ball; // イメージオブジェクトを入れる変数 private Image ballImage; public MyPanel() { setBackground(Color.white); ball = new Ball(100,100,1,1,0,0,580,400); // ファイルを読込みイメージオブジェクトを生成 try { ballImage = ImageIO.read(new File("ball.png")); } catch(IOException e) { System.out.println("ファイルを読み込めません。"); } Thread refresh = new Thread(this); refresh.start(); } public void paintComponent(Graphics g) { super.paintComponent(g); ball.forward(); // 座標を指定しイメージオブジェクトを表示 g.drawImage(ballImage, ball.getX(), ball.getY(), null); } public void run() { while(true) { repaint(); try { Thread.sleep(10); } catch(Exception e) { } } } } class Ball { private int x; private int y; private int vx; private int vy; private int left; private int right; private int top; private int bottom; public Ball(int x, int y, int vx, int vy, int l, int t, int r, int b) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; right = r; left = l; top = t; bottom = b; } public void forward() { if (x + vx < left || x + vx > right) vx = -vx; if (y + vy < top || y + vy > bottom) vy = -vy; x = x + vx; y = y + vy; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
下線で示した部分は、 外部からの画像を読込み表示するために必要な記述です。