Java による UI プログラミング: MICS 実験 2-19-1c＃ 01 Java によるグラフィクスプログラミング
久野靖∗

2019.10.7

0

ガイダンス

0.1

本テーマの概要

本テーマ「Java 言語によるユーザインタフェースプログラミング」では、Java 言語のオブジェクト指向機能を活用してさまざまなユーザインタフェースを作ってみていただきます。単に GUI 部品を使って入出力を行うのでは新しいことを作り出す面白さに欠けるので、そのような内容は最小限にとどめ (まったくやらないかも)、裸の描画機能と入力イベントからインタフェースを組み立てることを中心にします。
UI プログラミングはさまざまな言語で行うことができますが、ここでは強い型により誤りの発見が容易で、クラス方式のオブジェクト指向により画面上の「もの」が素直に表現でき、さらに Unix、Mac、Windows のどれでも動作させられるということから、 Java 言語を採用します。
Java 言語の処理系は適宜入手して自分のマシンに入れておいてください。J2SE (Java 2 StandardEdition) と呼ばれる、コンパイラと Java 仮想マシンが入ったパッケージがあればよいです。 Mac には最初から入っているはずです。
進め方については毎回、その回のテーマを典型的にあらわす例題プログラムを提示して説明をおこない、それを手直しして自分独自のものを作る演習課題をやって頂きます。その上で最後には、これまでにない「自分だけの」インタフェースを (簡単なものでもいいので) 構築していただき、相互に鑑賞会を行います。
教科書はとくに指定せず、毎回上記の例題を掲載した簡単な資料を配布しますが、もし考書が必要な場合は「久野禎子, 久野 靖, Java によるプログラミング入門 第 2 版, 共立, 2011」をおすすめします。その他、疑問等ありましたらいつでも y-kuno@uec.ac.jp までどうぞ。

0.2

成績評価

成績は次の 3 つに基づいてつけます。

A 上述のように、毎回授業時間に演習課題をやっていただきますので、その中から 1 つ「プログラムのみ」出していただきます。ただ、フィードバックも欲しいので末尾に「その回の感想」もつけていただきます。メールで提出してください。これは基本的に出席のようなものですが、以下の B、C の評価がよくないときに救済に使うこともあり得ます。
B 上述のように、最終回は「作品発表会」で「相互評価」しますので、その結果を評価に使用します。
C 作品発表したものについてレポートを提出してください。期限は「11 月末日まで」とします。
レポートには次のものが含まれること。 PDF 形式のファイルをメール送付してください。
∗電気通信大学1• 表紙。学籍番号、氏名、提出日付、作品のタイトル。(表紙と本体が別のページになっている必要はとくにありません。 LaTeX のタイトルとかを想定。 )• 構想。どのような作品を構想したか、どのように実装を設計したか。
• ソースコードとその説明。どの部分が何をしているか、どのような構造になっているか、など。
• 動作しているところの画面とその説明。
• 考察 (作品を作ってみて分かったことを書く)。
• 感想 (なんでも好きなことをどうぞ)。

1

Java 言語によるグラフィクス

1.1

最初の例題: 円を描く

図 1: 最初の例題を動かしたところ
では最初の例題として、Java で窓を開き、その中に円を表示する、というのをやってみましょう。

import java.awt.*;
import javax.swing.*;
public class Sam11 extends JPanel {public void paintComponent(Graphics g) {g.setColor(new Color(200,220,100));
g.fillOval(150, 70, 60, 60);}
public static void main(String[] args) {JFrame app = new JFrame("Test");app.add(new Sam11());
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setSize(600, 600);
app.setVisible(true);}}
一通り説明します。

1. Java では標準ライブラリにあるさまざまなクラスを利用しながらプログラムを書きますが、いきなりクラス名で指定できるようにすると多数のクラスがあって混乱するので、使用したいクラスやパッケージを import により指定します。今回は java.awt パッケージの各クラス、javax.swing の各クラスを名前だけで指定できるようにしました。たとえば JPanel とういの2

は正式名では javax.swing.JPanel ですが、import により短い名前だけで指定できるようになっているわけです。

2. Java ではすべてのプログラムはクラスと呼ばれる単位で構成します。今回は Sam11 とういクラスで、前述の JPanel(窓の中にはめ込める汎用的な部品) の機能を継承したクラスです。クラス名とファイル名は一致する必要があるので、この例題は必ず「Sam11.java」というファイルに保存してください。
3. 次は順番を少し変えて main を読みます。Java ではプログラムの実行は main という名前の static
メソッド (クラスのオブジェクトを作らなくても呼び出せるメソッド) から実行開始します。このメソッドは public(外部公開) で、かつパラメタとして String[](文字列の配列) を受け取る必要があります。
4. main の中ですが、まず JFrame という「窓」を表すオブジェクトを作ります。次に、その窓の中に Sam11 のインスタンス (クラスに所属するオブジェクト) を生成してはめ込みます。そして、窓枠で「×」印をクリックしたらプログラムが終了する設定にし、窓サイズを 600x600 にし、表示します。これで main は終了ですが、窓が閉じるまではプログラムは動作し続けます。
5. 最後に paintComponent を読みます。このメソッドは、この Sam11 オブジェクトの中身が表示されようとしているときに自動的に呼び出されます (main で窓にはめ込んで窓を開いたので、開いた後に JFrame の機能により呼び出されるわけです。開く前に内容を描こうとしてもできないので、このように「呼ばれて何かする」形になっているわけです。
6. paintComponent はパラメタとして Graphics オブジェクトを渡されます。このオブジェクトは「画面に描くためのペン」のようなもので、これに対してメソッドを呼び出すことで色々な描画ができます。ここでは色を RGB 値で「200,220,100」のものに設定したあと、その色で円を描いています。 fillOval のパラメタは、描く (正確には指定されている色で塗る) 楕円に外接する長方形の左上隅の XY 座標と幅、高さです。ここでは幅と高さを同じにしているので、円になるわけです。
動かし方ですが、まずプログラムは先に説明した通り、クラス名と (大文字小文字まで含めて) 同じ名前の「Sam11.java」というファイルに格納する必要があります。エディタ等で作業してください。動かすのは次の通り (動いたようすを図?? に示しました)。

% javac Sam11.java ← コンパイル。何も表示されなければ OK% java Sam11 ← 実行。実行時には「クラス名」を指定する。

1.2

API ドキュメント

Java 言語は豊富な標準ライブラリでも知られています。ライブラリのドキュメントも整備されており、以下のサイトで見ることができます。この文書は API ドキュメントと呼ばれています。 1
http://docs.oracle.com/javase/8/docs/api/
ライブラリはクラスの集まりであり、パッケージ (java.util.*とか java.lang.*とか) の単位でグループ化されています。 2
図??は API ドキュメントで java.lang.String のところを表示したものです。特定クラスを表示するのは、左下の全クラスの ABC 順の一覧から選ぶか、まず左上のパッケージ一覧でパッケージを選び、次に左下でそのパッケージのクラス一覧から選択します。String の例で分かるように、APIドキュメントではクラスごとにどのようなクラスかという説明と、それに続いてそのクラスが持つメソッドやその機能の説明が書かれています。
1
2

API は applicaiton programming interface の略。
java.lang.*は言語の基本機能を実現するクラス群なので、自動的に import されることになっている。

3

図 2: API ドキュメントの画面
演習 0 JFrame、JPanel の API ドキュメントを確認しなさい (javax.swing パッケージ内にあります)。また、Color、Graphics の API ドキュメントを確認しなさい (java.awt パッケージ内にあります)。このあと演習 1 をやるときに必要なら再度参照すること。
演習 1 Sam11 の例題をそのまま動かしてみなさい。動いたら円の色、大きさ、位置を変更してみなさい。またできれば、円を 2 つにしてみるとなおよいでしょう。窓の大きさを変更したりして起きることも観察すること。納得したら次のことをやってみなさい。

a. 次の図のような配置を作ってみなさい。色や配置の細かいところは好きにしてよい。
(1)

(2)

(3)

(4)

b. Graphics クラスの fillRect を使って正方形や長方形が描けることを確認し、次のような配置を作ってみなさい。
(1)

(2)

(3)

c. Graphics クラスのその他の機能も調べて使ってみなさい。
d. これまでに調べたり学んだことを活用して好きな画面表示をしてみなさい。

4

2

自前のクラスを作る

2.1

最初の例題の「クラス化」

前の例題で確かに色々な絵は描けましたが、そのやり方では画面上の円は単に「描画メソッドを呼んだ結果」であり、たとえば「円をクリックしてドラグする」みたいな操作には不向きです。では、どうしたらよいのでしょうか?

図 3: 2 番目の例題を動かしたところ
それには、画面上の「円」をプログラムの上でも「円」というオブジェクトにすることです。つまり、人間が思っているモデルとプログラム上のモデルを一致させることで、人間にとって自然な操作がプログラム上でも素直に記述できます。これがオブジェクト指向プログラミングのよいところです。
このため、先の例題を「円がクラスであるように」改良した例題を示します。画面は図?? のようにこれまでと変わりないのですが、プログラムの構造が違うということです。
こんどはクラスの先頭で Circle というクラスのインスタンスを 2 つ作って、変数に入れておきます。コンストラクタで色、XY 座標、半径を指定することで「円の属性」をまとめて設定しています。
そのため paintComponent では、単にそれぞれの円に対して Graphics オブジェクトを渡し「このペンで自分を描いてね」と言うだけです。ずっとすっきりしていますね。そして main は変わっていません (クラス名を除いては)。

import java.awt.*;
import javax.swing.*;
public class Sam12 extends JPanel {Circle c1 = new Circle(Color.MAGENTA, 100, 50, 30);
Circle c2 = new Circle(Color.BLUE, 190, 90, 40);
public void paintComponent(Graphics g) {c1.draw(g); c2.draw(g);}public static void main(String[] args) {JFrame app = new JFrame("Test");app.add(new Sam12());
app.setSize(600, 600);
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setVisible(true);}static class Circle {Color col;5

int xpos, ypos, rad;public Circle(Color c, int x, int y, int r) {col = c; xpos = x; ypos = y; rad = r;}public void draw(Graphics g) {g.setColor(col);g.fillOval(xpos-rad, ypos-rad, rad*2, rad*2);}}}
そしてもちろん、クラス Circle は自分で用意します。Circle は色と XY 座標と半径をインスタンス変数として持ち、コンストラクタでこれらを初期化し、 draw() では渡された Graphics オブジェ
クに対してこれまでと同様に fillOval() を呼んで円を描いています。
それで、プログラムの構造はどのように改善されたでしょうか? Circle というクラスを独立させて作ったことで、本体部分が簡潔になり、また本体と本体が使う部品のクラス Circle を分けて考えられるので、プログラムを検討するときに一時に見なければならない範囲が小さくなりました。
また、インスタンス変数は原則としてそのクラスのメソッドからだけアクセスされるので、それらの変数を書き換える箇所が限定でき、その範囲だけをよく検討することで間違いのないプログラムが作りやすくなります。このように、内部のデータ構造をカプセル化 (encapsulation) して外部からの干渉を排除することを情報隠蔽 (information hiding) と呼び、オブジェクト指向の重要な特徴となっています。
演習 2 例題 Sam12.java をそのまま打ち込んで動かしなさい。動いたら、次のように手直ししてみなさい。

a. 円の色や位置や大きさを変更してみなさい。たとえば、半透明な色を持つ円にしてみる、同心円状に配置するなど。
b. 円の数を 3 つ、4 つに増やしてみなさい。
c. 「長方形」を現すクラス Rect を追加し、円と一緒に表示させてみなさい。作成時のパラメタとしては「色、中心の XY 座標、幅、高さ」を指定するものとします。
d. 「三角形」を現すクラス Triangle を追加し、円と一緒に表示させてみなさい。作成時のパラメタとしては「色と 3 頂点の XY 座標」を指定するものとします。

2.2

複合図形を組み立てるクラスを作ることでそれが部品になる、という話をしましたが、それを実感できる例題として「部品どうしを組み合わせた図形を作る」ことを考えましょう。たとえば、図??のような図柄の旗を作るのに、3 「長方形」という部品と「円」という部品を組み合わせて作るわけです。そしていちど旗クラスを定義すれば、図??のように複数の旗を表示することができます。
では、例題を見ましょう。まず、クラスができたものとして、それを使う部分はこれまでとほとんど変わりません。 4 クラス Circle は前と同じです。そして、長方形を表わすクラス Rect を同様に作っています。

import java.awt.*;
この図柄のような旗が国旗になっている国もいくつかありますね。
Flag のコンストラクタは「new Flag(地の色, 丸の色, 丸の半径, X 座標, Y 座標)」という呼び出し方にするものとしました。
346r4r6r

図 4: 旗の図柄の設計図

図 5: 2 つの旗

import javax.swing.*;
public class Sam13 extends JPanel {Flag f1 = new Flag(Color.GREEN, Color.BLUE, 30, 100, 80);
Flag f2 = new Flag(Color.GRAY, Color.RED, 20, 240, 120);public void paintComponent(Graphics g) {f1.draw(g); f2.draw(g);}public static void main(String[] args) {JFrame app = new JFrame("Test");app.add(new Sam13());
app.setSize(600, 600);
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setVisible(true);}static class Circle {Color col;int xpos, ypos, rad;public Circle(Color c, int x, int y, int r) {col = c; xpos = x; ypos = y; rad = r;}public void draw(Graphics g) {g.setColor(col);g.fillOval(xpos-rad, ypos-rad, rad*2, rad*2);}

7}
static class Rect {Color col;int xpos, ypos, width, height;
public Rect(Color c, int x, int y, int w, int h) {col = c; xpos = x; ypos = y; width = w; height = h;}public void draw(Graphics g) {g.setColor(col);g.fillRect(xpos-width/2, ypos-height/2, width, height);}}
static class Flag {Rect r1;Circle c1;
public Flag(Color cl1, Color cl2, int r, int x, int y) {r1 = new Rect(cl1, x, y, r*6, r*4);
c1 = new Circle(cl2, x, y, r);}public void draw(Graphics g) {r1.draw(g); c1.draw(g);}}}
最後に旗のクラス Flag ありますが、簡単ですね? つまり、旗というのは長方形と円から成っているので、その 2 つのオブジェクトをコンストラクタで生成してインスタンス変数として保持し、表示時もこれらを表示すればいいわけです。 5 そして、長方形と円のクラスは先に作成した通りです。このように、既に作ったクラスは部品として、それらを組み合わせて複合図形を作ることで、次々に複雑な絵を作ることができます。
演習 3 既に作った長方形、三角形、円のクラスを利用して下の図のような複合図形のクラスを作成しなさい。大きさは図の u の長さを指定し、色の塗り分け方は適宜判断してください。完成したら、さらに自分で設計した独自の図形も作ってみるとよいでしょう。
uu(a)

(b)

(c)

演習 4 絵の中に「文字」を表示したいことがよくあります。文字は Font クラスを使って表示フォン
トを設定し、drawString() で描画することで表示できます。例題を次に示すので、これを参考に「文字表示クラス」を作成しなさい。
描く時には、まず長方形を描いてから円を描くという順序が大切です。後から描いた絵が上書きになるので、長方形を後にすると円が消されてしまいます。
58

import java.awt.*;
import javax.swing.*;
public class Sam14 extends JPanel {public void paintComponent(Graphics g) {Font fn = new Font("Serif", Font.BOLD, 20);
g.setFont(fn); g.drawString("日本", 100, 100);}
public static void main(String[] args) {JFrame app = new JFrame("Test");app.add(new Sam14());
app.setSize(600, 600);
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setVisible(true);}}9