• 検索結果がありません。

java14.dvi

N/A
N/A
Protected

Academic year: 2021

シェア "java14.dvi"

Copied!
14
0
0

読み込み中.... (全文を見る)

全文

(1)

Swing

パッケージ

久野

2006.1.17

はじめに

今日は最初に、自作のアプリケーション窓を充実させるために、幾つかの、まだ使っていないクラスを使い方を簡単 にお見せします。。そのあと今日の本題は、Swing パッケージの混み入った GUI 部品で有用な「モデルとインタフェー スの分離」の考え方を説明し、具体的なプログラムで見てみましょう。

1

レイアウトマネージャ

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

public class R14Sample1 extends JFrame {

JScrollPane a1 = new JScrollPane(new JTextArea()); JButton b1 = new JButton("Quit");

public R14Sample1() {

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(400, 300); Container c = getContentPane(); c.setLayout(null);

c.add(a1); a1.setBounds(20, 20, 340, 200); c.add(b1); b1.setBounds(20, 240, 340, 40); b1.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent evt) { System.exit(0); } });

}

public static void main(String[] args) { (new R14Sample1()).setVisible(true); } } 今日の最初の例題は、中央にスクロールバーつきのテキスト入力欄、下にボタンを配置し、ボタンが押されるとプロ グラムが終了するというだけものである (あと窓を閉じても終わる)。 ところで、上のプログラムは普通の窓を開くので、窓の大きさはマウス操作で自由に変更できる。しかしそうした場 合、部品の位置を座標で配置すると窓の大きさを変更したときに切れたり余白ができたりして悲しいことになるのはよ くご存じですね? それを避けるには、ウィンドウの大きさを変更したとき、領域の大きさに応じて部品の配置を調整す る必要があるわけだ。つまり、アプレットでは領域の大きさは固定だったが、フレーム窓は窓の大きさに追従するよう にしないと困るわけだ。 ここでいよいよ、これまで setLayout(null) でわざわざ無効にしてきた自動配置機能を使うことにしよう。Java で はこの自動配置機能をレイアウトマネージャと呼び、「縦横にます目に並べる」「一列に並べる」「上下左右と中央に置く」 などさまざまな「並べ方」のレイアウトマネージャが用意されている。そして、特に指定しなければ (setLayout(null)

(2)

をやらなければ)、BorderLayout という名前の「上下左右と中央に置く」レイアウトマネージャが動作するようになっ ている。 具体的には、BorderLayout では、ウィンドウ内に部品を配置するときに図 1 に示すように「東西南北または中央」と 位置が指定でき、それぞれに指定された部品はその位置を占めるように自動的に配置してもらえる。 South North East West Center または 無指定 図 1: BorderLayout の指定方法 ではこれを利用して中央に JTextArea、その下にボタンを自動配置するように直したものを示す。 import java.awt.*; import java.awt.event.*; import javax.swing.*;

public class R14Sample2 extends JFrame {

JScrollPane a1 = new JScrollPane(new JTextArea()); JButton b1 = new JButton("Quit");

public R14Sample2() {

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container c = getContentPane();

c.add(a1); c.add(b1, BorderLayout.SOUTH);

// c.add(new JButton("Button 1"), BorderLayout.NORTH); // c.add(new JButton("Button 2"), BorderLayout.EAST); // c.add(new JButton("Button 3"), BorderLayout.WEST);

pack(); setSize(400, 300);

b1.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent evt) { System.exit(0); } });

}

public static void main(String[] args) { (new R14Sample2()).setVisible(true); } } このプログラムでは、pack() で自動配置を開始した後、最後に窓の大きさを設定している (こうしないと各部品が入る 最小の窓の大きさになってしまう)。また、コメントアウト部分を含めると上と左右にもボタンが現れ、BorderLayout の機能がよく分かる。

1.1

もうちょっと複雑な配置をしたいときは…

しかし、上の方法だとたとえば下側にボタンを 4 つ並べようと思ってもうまく行かない。そのような場合は、JPanel を「貼りつけ用部品」として利用する。すなわち、JPanel を作って、その上に「ボタンを順番に並べる」には今度は

(3)

FlowLayout という単に順番にならべるだけのレイアウトマネージャを設定してからボタンを追加していく。JPanel 自 体は外側のウィンドウの下に詰めればよい。

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

public class R14Sample3 extends JFrame {

JScrollPane a1 = new JScrollPane(new JTextArea()); JButton b1 = new JButton("Quit");

JPanel p1 = new JPanel(); public R14Sample3() {

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container c = getContentPane();

c.add(a1); c.add(p1, BorderLayout.SOUTH); p1.setLayout(new FlowLayout()); p1.add(b1); p1.add(new JButton("Button 1")); p1.add(new JButton("Button 2")); p1.add(new JButton("Button 3")); pack(); setSize(400, 300); b1.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent evt) { System.exit(0); } });

}

public static void main(String[] args) { (new R14Sample3()).setVisible(true); } } このようにして、窓の大きさが変わっても配置が自動的に調整でき、しかもある程度自分で配置を制御できるようになっ たわけだ (一応は…)。なお、レイアウトマネージャはここで学んだ 2 つのほかにまだ数個ある。興味があったら API ドキュメントや、そこからリンクされているチュートリアルで調べてみるとよい。また、レイアウトマネージャ以外に JTabbedPane、JSplitPane などを使ってタブで切り替えたり画面を2 分割するなどの部品も「いれものの配置」という 点では似ているかも。 演習 上のプログラムを打ち込んでそのまま動かせ。動いたら次の演習をやってみよ (API ドキュメントを熟読する必要 あり)。 a. GridLayout を使ってボタンを複数個配置してみる。 b. JTabbedPane を使ってタブで切り替える画面を作ってみよ。 c. その他自分の好きなレイアウトマネージャや Swing の部品を試してみよ。

2

JFrame

とメニュー関係の機能

ところで、ここまでに出て来た例題は、窓はできるものの、普段使っているアプリケーションと比べてだいぶ「淋し い」ですよね? 普通のアプリケーションであれば、ボタンではなくメニューバーとメニューを使いますよね? 一応この あたりの機能も説明しておこう。

• JFrame のインスタンスメソッド setJMenuBar() で JMenuBar オブジェクトを設定することで窓にメニューバーが つけられる。

(4)

• JMenuBar に対してはインスタンスメソッド add() で JMenu オブジェクトを設定することで複数のプルダウンメ ニューが設定できる。

• JMenu に対してはインスタンスメソッド add() で JMenuItem オブジェクトを設定することでメニュー項目を追加 して行ける。またインスタンスメソッド addSeparator() で「区切り線」を追加できる。 • JMenuItem オブジェクトに対しては押しボタンと同様、addActionListener() で動作を設定できる。 • JOptionPane というクラスのクラスメソッド showMessageDialog() を呼ぶことで簡単にダイアログボックスが表 示させられる (その他の種類のダイアログも同様)。 なんだかよく分からないだろうが、ここまでの事柄を使った例題プログラムを見ていただこう。 import javax.swing.*; import java.awt.*; import java.awt.event.*;

public class R14Sample4 extends JFrame { JMenuBar mbar = new JMenuBar();

JMenu menu0 = new JMenu("File");

JMenuItem item0 = new JMenuItem("Red"); JMenuItem item9 = new JMenuItem("Quit"); JPanel panel = new JPanel();

public R14Sample4() {

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

menu0.add(item0); menu0.addSeparator(); menu0.add(item9);

mbar.add(menu0); setJMenuBar(mbar); getContentPane().add(panel); pack(); setSize(400, 300);

item0.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {

JOptionPane.showMessageDialog(R14Sample4.this, "paint red."); panel.setBackground(Color.red);

} });

item9.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) { System.exit(0); } });

}

public static void main(String[] args) { (new R14Sample4()).setVisible(true); } } 上で説明したことが具体的にどう使われているか見ていこう。 • クラスの先頭、インスタンス変数ではメニューバー、その中のメニュー、そのまた中のメニュー項目 2 つ、および パネル 1 つを保持している。 • メニューに項目 1、区切り線、項目 2 を追加。 • メニューバーにメニューを追加。 • 窓にメニューバーを追加 • 窓「中身」を getContentPane() で取得し、そこにパネルを追加。

(5)

• 項目 1 が選ばれた場合の動作として、ダイアログウィンドウでメッセージを表示し、パネルの背景を赤に設定する。 JOptionPane のクラスメソッド showMessageDialog() は第1 引数として窓 (この場合は R14Sample4 のインスタ ンス) を渡す必要があるが、単に this と書くと一番内側のクラス、つまり無名内部クラスのインスタンスを意味 してしまう。そこで外側クラスの this を指定したい場合は外側クラスの名前をつけて「R14Sample4.this」と指 定する。 • 項目 2 が選ばれた場合の動作としてプログラムを終了するようにする。 演習 上の例題を打ち込んでそのまま動かせ。動いたら次のように直せ。面倒がらずに API ドキュメントも参照すること。 a. メニュー項目を増やし、もっと別の色も設定できるようにせよ。

b. a. と同様だが、File メニューとは違うメニューを増やし、そこで色が選べるようにする。(ヒント: JMenu menu1 = new JMenu("Color"); などとしてインスタンス変数を増やし、これも mbar に追加する)。

c. 色を設定するとき「OK」ではなく「やめる」こともできるようにする。(ヒント: JOptionPane クラスの別の クラスメソッドを利用する。) d. メニュー項目として JCheckBoxMenuItem を使い、確認ダイアログの出る/出ないを制御できるようにする。 (ヒント: 前記のメニュー項目はメソッド getState() を呼ぶと ON か OFF か調べられる。)

3

さまざまな部品群

アプレットでもフレームでも、どちらで動かすプログラムでも GUI を作るのにお世話になる Swing の部品群の概要に ついてまとめておく。また Swing の部品群を使いこなすのに必要なモデルとビューの概念について学ぶ。

• JFrame、JButton、JLabel、JList、JTextArea、JPopupMenu — 従来の awt パッケージの Frame、Button、Label、 List、TextArea、PopupMenu などの Swing 版。Swing 以前からある部品。

• JSlider、JProgressBar、JComboBox — スライダー、プログレスバー、コンボボックス。 • JTabbedPane — タブつきパネル (タブを選択することで内容が切り替えられる)。 • JScrollPane — 中身が画面上の領域より大きいとき自動的にスクロールバーをつけてくれる。 • JSplitPane — 画面を縦または横に 2 分割してそれぞれに部品が入れられる。 • JTree — Windows のエクスプローラみたいなツリー表示を行う。 • JTable — 表計算ソフトみたいな表を作る。 他にもいろいろあるがだいたいこういう感じである。さまざまな GUI 部品をまとめて見せるデモ「SwingSet2」を見て いただきたい(ダウンロードした Java の処理系についてきているので、「java -jar SwingSet2.jar」コマンドで実行して 見られる)。さらに詳しくは市販の本などの解説を見たり、次のページ以下のチュートリアルなどを見てみてほしい。 http://java.sun.com/docs/books/tutorial/uiswing/

4

モデルとビューの分離

4.1

JTable

JTree、JTable などSwing 部品群の多くで採り入れられている重要な概念として「モデルとビューの分離」がある。 たとえば「表」というのは縦横のます目が並んだ「見た目」を持っているが、そのます目の中身というのはどのような 形で保管されていても構わないはずである。もちろん、実際に M × N 個の要素が並んだ配列 (2 次元配列) を中に持って いてもいいのだが、用途によってはそうでない方が便利かも知れない。 このため、JTable の場合、表の中身は TableModel というインタフェースを実装するオブジェクトとして表すことに なっている (図 2)。そのオブジェクトは、たとえば次のようなメソッドを持つ。 • int getRowCount() — 表の行数を返す • int getColumnCount() — 表の列数を返す

(6)

   ビュー (ユーザインタフェース)    モデル(普通のオブジェクト) 「何行何列の表だ」 「このセルの  内容は…」 「このセルを変更」 JTable TableModel を実装したオブジェクト 図 2: 表のビューとモデル • String getColumnName(i) — i 列目の列名を文字列として返す • boolean isCellEditable(r,c) — r 行 c 列目のセルをユーザが編集してもよいか否かを返す • Object getValueAt(r,c) — r 行 c 列目のセルに対応するオブジェクトを返す • void setValueAt(Object,r,c) — r 行 c 列目のセルの値として設定したいオブジェクトを渡して呼び出される そのほかにも多数のメソッドがあって全部用意するのは大変なので、おなじみ MouseAdapter 同様、AbstractTableModel というクラスが用意されていて、そのサブクラスを作るようにすれば自分が再定義したいメソッドだけを書けば済む (getRowCount()、getColumnCount()、getValueAt() の 3 つは抽象メソッドなので必ず書く必要がある)。 以上をまとめると、表を使いたい時は AbstractTableModel のサブクラスとして自分の好きな情報を保持するクラス を作り、それを JTable のコンストラクタに渡して表を作ることで、データが表の形で表示され、またユーザが表に書き 込む形でデータを更新することができる。具体例を見てみよう。 import javax.swing.*; import javax.swing.table.*;

public class R14Sample5 extends JFrame { public R14Sample5() {

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

getContentPane().add(new JScrollPane(new JTable(new MyTableModel()))); pack(); setSize(400, 150);

}

public static void main(String[] args) { new R14Sample5().setVisible(true); }

class MyTableModel extends AbstractTableModel {

double[] rows = new double[]{1, 3, 5, 7, 11, 13, 17, 19, 23}; double[] cols = new double[]{1, 2, 3, 4, 5, 6, 7, 8};

public int getRowCount() { return rows.length; } public int getColumnCount() { return cols.length; } public boolean isCellEditable(int r, int c) {

if(r == 0) return true; else return false; }

public Object getValueAt(int r, int c) { return new Double(rows[r] * cols[c]); }

public void setValueAt(Object o, int r, int c) { if(r == 0) {

cols[c] = new Double(o.toString()).doubleValue(); }

(7)

} } }

画面は JScrollPane の内側に入れた JTable だけで、そのインスタンスを生成するときに後で定義する MyTableModel のインスタンスを渡しているので、このモデルを表示する表が画面に現れる。

さて、結局このプログラムの「中心」は MyTableModel クラスということになる。その中では rows と cols という 2 つの配 列があり、それぞれが縦方向と横方向の列に対応する数値を保持している。従って、getRowCount()、getColumCount() はそれぞれの配列の length を返せばよい。また、一番上の 1 行だけはユーザがセルを変更できることにしたので、 isCellEditable() は行が0 の時だけ true を返す。そして、各セルに表示される値は rows[r] と cols[c] の積、つま りこの表は単なる「かけ算」の表。なお、getValueAt() は積の値を返すが、返すのは何らかのオブジェクトでなければ ならないので、Double クラスのインスタンスとして返す。

データが変更された時の処理は、変更されるのは 1 行目だけだが一応そのことをチェックし、渡されたものを文字 列に変換し、double に変換し、そしてそれを cols[c] に入れる。つまりユーザが cols[c] の値を自由に変更でき るわけだ。データを変更した場合は、表示全体が変わるので AbstractTableModel から継承している自分のメソッド fireTableDataChanged() というメソッドを呼ぶことで JTable 側で表示を変更してくれる(グラフィクスの repaint() のようなものと思えばよい)。 このように、「モデルとユーザインタフェース (表示部分) を分離する」という考え方は、プログラムの構造が整理され る (半分ずつに分けると作成の労力はそれぞれが元の 1/4 くらいずつになると言われている)、1 つのモデル (計算処理) でユーザインタフェースだけ取り替えることができる、などの利点があるので、機会があれば活用してみることをお勧 めする。具体的には次のように考えるとよい。 • 自分がしたい計算部分を「外からアクセスする」にはどのようなメソッドがあれば十分かを考え、それを Java の インタフェースとして定義してやる。 • 計算部分はそのインタフェースを implements するクラスとして作る。 • ユーザインタフェース部分はそのインタフェースを implements したオブジェクトを最初に受けとり、そのメソッ ドを呼び出しながら表示や入力を行うように作る。 演習 上の例題をそのまま動かせ。動いたら次のように変更してみよ。 (a) 上の例では「積」の表だったが、「和」や「差」など別の種類の演算の表に直してみよ。 (b) 上の例では一番上の列だけ変更可能だったが、加えて一番左の列も変更可能にしてみよ。ただし rows[0] は 1 のまま変更しないものとしてよい。 (c) 上に加えて、「任意の」位置が変更できるように直してみよ (その書き換えた位置の値から逆算して cols[i] または rows[i] を適切な値に直す)。 (d) 一番下左のセルを書き換えた場合、その書き換えた数値をもとに「その数値以下のすべての素数」が左の列に 並ぶように表を変更するように直してみよ (もちろん、表の行数が変化することになる)。 (e) 住宅ローン計算プログラムを作れ。最初に借りた金額と、毎年の返済額と、年利をどこかのセルに入力させ る。毎年、前年の債務残高から返済額を引き、債務残高に年利を書けた値を加えたものが翌年の債務残高にな るわけ。 (f) 表を使って何か面白いプログラムを作れ。

4.2

DefaultTableModel

上のようにして自前でモデルを作るのはおおむねよい方法だが、単に値を入れておきたいだけの場合には「おまか せ」でただの 2 次元配列のようなものがあると便利である。そのために、javax.swing.table パッケージの中には DefaultTableModel というクラスが用意されている。これを利用して先の例題を手直しし、「九九の表」にしたものを 見てみよう。 import javax.swing.*; import javax.swing.table.*;

(8)

public class R14Sample6 extends JFrame {

TableModel tab = new DefaultTableModel(9, 9); public R14Sample6() {

for(int i = 1; i < 10; ++i) {

for(int j = 1; j < 10; ++j) tab.setValueAt(new Integer(i*j), i-1, j-1); }

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

getContentPane().add(new JScrollPane(new JTable(tab))); pack(); setSize(400, 150);

}

public static void main(String[] args) { new R14Sample6().setVisible(true); } } 非常に簡単だが、ただし表に記入するとその値が入ったままになる (当然)。なおこのクラスを extends して自分用のモ デルを用意することもできる。

5

ファイルを読む

GUI

プログラム

5.1

ファイルの指定と読み込み

アプレットでは禁止されているが、JFrame のアプリケーション窓でならできることに、ファイルの利用がある。ファ イルを利用するための GUI 部品はまだ紹介していなかったので、ファイルから内容を読み込んで表示する GUI プログラ ムを作ってみよう。その前に復習だけど、ファイルから 1 行ずつ読み込むためには、次のオブジェクトの機能を利用する のだった。ファイル関係の GUI 部品を使っても、ファイルを扱う部分以降のプログラムは(もちろん)そのままである。 • FileInputStream — ファイルを指定して、バイト単位で読み込む機能を提供する。 • InputStreamReader — (日本語などの) 文字単位で読み込む機能を提供する。 • BufferedReader — 行単位で読み込む機能を提供する。 ではさっさとプログラム例を示そう。このプログラムでは、ウィンドウ内に JList オブジェクトを 1 個だけ配置し、そ の JList オブジェクトに読み込んだファイルの内容を全部 add() することで表示させる。ファイルが長い場合には List オブジェクトが適当にスクロールバーを用意してくれるのでとっても便利である。

読み込むファイルを指定するのには、部品 JFileChooser を利用する。この部品は showOpenDialog でファイル選択 窓を作るときに「親にあたるウィンドウ」を指定しなければならない。それは現在作っている窓が相当する。一般にクラ スの中で「this」という名前で「このオブジェクト (自分)」を参照することができるので、ここでも this を指定する。

JFileChooser の使い方は、showOpenDialog のファイル選択窓の消去時に返された状態が APPROVE OPTION の時、ユー ザが選択した File オブジェクトが JFileChooser に渡されています。

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

public class R14Sample7 extends JFrame {

DefaultComboBoxModel m0 = new DefaultComboBoxModel(); JFileChooser fc = new JFileChooser();

JList l0 = new JList(m0);

JScrollPane s0 = new JScrollPane(l0); public R14Sample7() throws Exception {

(9)

Container c = getContentPane(); c.setLayout(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); c.add(s0); s0.setBounds(10,30,320,260);

pack(); setSize(400, 300);

int returnVal = fc.showOpenDialog(R14Sample7.this); if (returnVal == JFileChooser.APPROVE_OPTION) {

BufferedReader in = new BufferedReader(

new InputStreamReader(new FileInputStream(fc.getSelectedFile()), "JISAutoDetect"));

String line;

while((line = in.readLine()) != null) { m0.addElement(line); } in.close();

}

l0.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent evt) {

if(l0.getSelectedIndex() >= 0)

m0.removeElementAt(l0.getSelectedIndex()); }

}); }

public static void main(String[] args) throws Exception { new R14Sample7().setVisible(true);

} }

読み込んで表示するだけで何もしないのはつまらないので、JList 部品の中をマウスで選択すると選択された行が消えて なくなるようにした。JList に入れるモデルは、ListModel インタフェースを実装するものを採用しなくてはいけないが、 API を見て実装クラスの DefaultComboBoxModel を用いた。この JList に行を全部つっこむ。このモデルに対して、変 更イベント来たら選択箇所を削除するように実現してみた。あと、表示するファイルは行が多いかもしれないので、ス クロールバー出すために JList を JScrollPane に入れないといけない。 演習 この例題が動いたら、次のように改良してみよ (よく分からない機能については API ドキュメントの java.awt.Frame、 java.awt.event.WindowListener などの項目をチェックするとよい)。 a. 「Quit ボタン」も追加し、このボタンを押すことでも終われるようにする。 b. アイコン化するとすぐまた開いてしまうようにする。 (ヒント: ウィンドウアダプタにメソッド windowIconified() を追加し、そこで setState(Frame.NORMAL); を実行する。) c. マウスが窓の上に来たとたんにアイコンになってしまうようにする。 (ヒント: マウスアダプタにメソッド mouseEntered() を追加するし、そこで setState(Frame.ICONIFIED); を実行する。) d. 選択した行をすぐ削除するのではなく、「削除」ボタンを押すと削除し、「コピー」ボタンを押すと同じ行を 2 つコピーし、「先頭」ボタンを押すとその行を先頭に移すようにする。 e. 加工した結果をファイルに保存できるようにする (次節ヒント参照)。

5.2

ちなみに出力のときは…(復習)

上の例題には出てこなかったが、ファイルに出力したいときも一応復習。 PrintWriter pw = new PrintWriter(

(10)

...

pw.println(...); // これを繰り返して各行を出力 ...

pw.close();

ここで「☆」のところにはファイル名 (String オブジェクト) を指定し、「符号化」としては次のどれかを指定する。 • "UTF8" — UNICODE UTF-8。

• "EUC JP" — 日本語 EUC。 • "SJIS" — Shift-JIS。 • "ISO2022JP" — 標準の JIS7 ビットコード。

5.3

画像ファイルを扱う例題

今日はファイルをやったので、外部ファイルにある画像を取り扱う例題をひとつ載せておこう。ただしこれは JDK 1.4 で入った機能なので少し古い処理系だと使えないのに注意。この機能のためのクラスは ImageIO といい、パッケージ javax.imageio に入っている(ので、import を追加する)。使い方は非常に簡単:

BufferedImage img = ImageIO.read(File); // 読む ImageIO.write(BufferedImage, 種別, File); // 書く

ここで File というのは java.io パッケージに含まれているクラスで、ファイル名を表す。普通は new File(文字列) で ファイル名かパス名を渡して作ればよい。「種別」は書き出す画像の形式を指定する文字列で、通常「"jpg"」(JPEG) か 「"png"」(PNG 形式) を指定する (JPEG は出力時の制約があるので、PNG をお勧めする)。GIF はライセンス問題1

あったため、当面は書き出し機能を持たないようにしている。

また、read でファイルから読み込んだ画像の型、あるいは write の第 1 引数のファイルに書き出す画像の型が BufferedImage であるが、これが Java で画像を扱うデータ型である。BufferedImage クラスは java.awt.image パッケージに含まれ ているので、この import 文も指定するのを忘れないこと。 では実例がないとつまらないので、画像にラクガキするソフトを作ってみよう。 import java.io.*; import java.awt.*; import javax.swing.*; import javax.swing.event.*; import java.awt.event.*; import java.awt.image.*; import javax.imageio.*;

public class R14Sample8 extends JFrame {

BufferedImage img = new BufferedImage(300,200,BufferedImage.TYPE_INT_ARGB); JButton b0 = new JButton("Load");

JButton b1 = new JButton("Save"); JLabel l0 = new JLabel();

JPanel p0 = new JPanel() {

public void paint(Graphics g) { g.drawImage(img,0,0,this); } };

JScrollBar[] s = new JScrollBar[]{new JScrollBar(), new JScrollBar(), new JScrollBar(), new JScrollBar()}; JFileChooser fc = new JFileChooser();

1GIF に使われている圧縮方式の特許を持っている Unisys 社が、最初は使用料を取らなかったのに GIF が広く使われるようになった頃に突然「こ

(11)

public R14Sample8() {

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Graphics g = img.getGraphics();

g.setColor(Color.white); g.fillRect(0, 0, 200, 500); Container c = getContentPane(); c.setLayout(null); c.add(b0); b0.setBounds(10, 30, 80, 30); c.add(b1); b1.setBounds(10, 80, 80, 30); c.add(l0); l0.setBounds(80, 30, 300, 30); c.add(p0); p0.setBounds(100, 100, 300, 500); pack(); setSize(400, 600); for(int i = 0; i < 4; ++i) { c.add(s[i]); s[i].setBounds(10+i*20, 120, 15, 256); s[i].setMaximum(255); s[i].setMinimum(0); } b0.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) {

try {

int returnVal = fc.showOpenDialog(R14Sample8.this); if (returnVal == JFileChooser.APPROVE_OPTION) { img = ImageIO.read(fc.getSelectedFile());}

} catch(Exception ex) { l0.setText(ex.toString()); } }

});

b1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) {

try {

int returnVal = fc.showOpenDialog(R14Sample8.this); if (returnVal == JFileChooser.APPROVE_OPTION) { ImageIO.write(img, "png",

fc.getSelectedFile());}

} catch(Exception ex) { l0.setText(ex.toString()); } }

});

addMouseListener(new MouseAdapter() {

public void mousePressed(MouseEvent e){put(e.getX()-100,e.getY()-100);} });

addMouseMotionListener(new MouseMotionAdapter() {

public void mouseDragged(MouseEvent e){put(e.getX()-100,e.getY()-100);} });

}

public void put(int x, int y) { Graphics g = img.getGraphics();

g.setColor(new Color(s[0].getValue(), s[1].getValue(), s[2].getValue(), s[3].getValue())); g.fillOval(x-10, y-10, 20, 20); repaint();

(12)

public static void main(String[] args) throws Exception { new R14Sample8().setVisible(true); } } このプログラムは最初は真っ白な画像を 1 枚保持しているが、Load ボタンを使って別の画像を読み込むことができる。 ラクガキした後で画像を保存するには Save ボタンを使う。ラクガキはマウスでクリック/ドラグすることで行うが、そ の色はスクロールバーで設定できる。この辺はおなじみの通り。 しかし何でスクロールバーが 4 本あるのか? 実は! クラス Color のコンストラクタには 4 引数のものがあり、4 番目 の値として「色の透明度」を 0∼255 の整数で指定できる。つまり半透明な色で描くと前の状態に「透けた感じで重ね描 き」できるわけだ。先に出て来た BufferedImage の種別「TYPE INT ARGB」の「A」は「alpha (α)」、つまり透明度を 表しているのだった。 演習 この例題プログラムが、動いたら次のように改良してみよ。 a. 「ペン先」の大きさを色々変化できるようにする。 b. 「ペン先」の形を円以外に変えられるようにする。 c. 別の画像 (小さいのがいい) を読み込んで来てペンとして使えるようにする。 d. その他自分でいいと思う改良を施す。

6

14

回演習

次のような3科目ぶんのテストの素点を入力したデータファイルを読み込み、各生徒の素点、各科目の平均点、各生 徒の合計点を表の形で画面に表示するプログラム Seiseki0.java がある(画面への表示と同じものを stable.txt というファ イルにも書き出す)。Seiseki0 は、ファイルから読み込んだデータを、プログラム内では2次元配列に保持して処理して いる。2 次元配列ははじめて使ったので、2次元配列の使い方を見てください。 2次元配列の使い方が分かったら、次に、Seiseki0.java を、表インタフェースを実現する JTable クラスを用いて作り 直してみなさい。そのついでに、ファイルの読み込みや保存の GUI 部品も活用し、より良いプログラムに作り直しなさ い。この作り直しの経験から、プログラムのインタフェース部分の設計や実現について、考えたことを述べなさい。 iida 76 45 66 久野 65 43 66 ・・・(以下同様のデータ) 空行  ← データファイルの末尾は必ず空行 import java.io.*;

public class Seiseki0 {

public static void main(String[] args) throws Exception {

BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("seiseki.data"), "JISAutoDetect"));

(13)

"};

String[] name = new String[100]; int[][] data = new int[100][4];

String head2[] = {"japanese", " english", " math ", " total "}; double[] average = new double[4];

//read data int count = 0; while(count < data.length) { //read name String nm = in.readLine(); if(nm.equals("")) break; String sp = ""; int k= 7 - nm.length();

for (int i=0; i<8; ++i) {sp = sp.concat(" "); if (i==k) break;}; name[count] = sp.concat(nm);

//get data and total int total = 0;

for (int i=0; i<3 ; ++i) {

int v = (new Integer(in.readLine())).intValue(); data[count][i] = v; total = total + v; } data[count][3] = total; ++count; } //get average

for (int i=0; i<4 ; ++i) { int stotal = 0;

for (int j=0; j<count ; ++j) { stotal = stotal + data[j][i]; }

average[i] = (double)stotal/count; }

//print tables

PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream ("stable.txt"), "SJIS"), true);

System.out.println("====="); System.out.println("exam"); System.out.println("====="); pw.println("====="); pw.println("exam"); pw.println("=====");

(14)

for(int i=0; i<5; ++i) {System.out.print(head1[i]);pw.print(head1[i]);}; System.out.println();

pw.println();

for(int i = 0; i < count; ++i) { System.out.print(name[i]); pw.print(name[i]); for (int j=1; j<5; ++j) { if (data[i][j-1] >= 100) {System.out.print(" " + data[i][j-1]); pw.print(" " + data[i][j-1]);} else if (data[i][j-1] < 10) {System.out.print(" " + data[i][j-1]); pw.print(" " + data[i][j-1]);}

else {System.out.print(" " + data[i][j-1]); pw.print(" " + data[i][j-1]);}; } System.out.println(); pw.println(); } System.out.println(); System.out.println("average:"); System.out.println(); pw.println(); pw.println("average:"); pw.println();

for(int i=0; i<4; ++i) {

System.out.print(head2[i] + " : " + average[i]); System.out.println(); pw.print(head2[i] + " : " + average[i]); pw.println(); } pw.close(); } }

参照

関連したドキュメント

WAV/AIFF ファイルから BR シリーズのデータへの変換(Import)において、サンプリング周波 数が 44.1kHz 以外の WAV ファイルが選択されました。.

線遷移をおこすだけでなく、中性子を一つ放出する場合がある。この中性子が遅発中性子で ある。励起状態の Kr-87

Instagram 等 Flickr 以外にも多くの画像共有サイトがあるにも 関わらず, Flickr を利用する研究が多いことには, 大きく分けて 2

画像の参照時に ACDSee Pro によってファイルがカタログ化され、ファイル プロパティと メタデータが自動的に ACDSee

ポンプの回転方向が逆である 回転部分が片当たりしている 回転部分に異物がかみ込んでいる

はありますが、これまでの 40 人から 35

つまり、p 型の語が p 型の語を修飾するという関係になっている。しかし、p 型の語同士の Merge

ある架空のまちに見たてた地図があります。この地図には 10 ㎝角で区画があります。20