last revised: Sep./13th/2002; Since Sep./09/2002
ここではより実用的なアプリケーションをSwingを使ってGUIを実装します。いままでの復習/応用として、Swing実装の手続きを把握してください。コンポーネントの最初にJがつくSwingコンポーネントには、AWTにカウンターパートのコンポーネントが存在し、これを軽量(light weight)コンポーネントとして拡張したものです。Swingアプリケーションで特徴的なことは、コンテント・ペインという概念です。Swingではトップレベルコンポーネントにコンポーネントを追加するのに、そのトップレベルコンポーネントのコンテント・ペインというものを中継します。コンテント・ペインの取得はgetPane()を用います。逆に言えば、イベント処理やコンポーネントのコンテナへの追加方法などはAWTを踏襲しています。
また、ここではAWTと同じレイアウト・マネージャを使っています。自身に他のコンポーネントを追加可能なコンポーネントのことをコンテナと呼びましたが、コンテナには追加するコンポーネントのレイアウト方法を指定するためのレイアウト・マネージャが指定可能であり、SwingではAWTで使えるレイアウト・マネージャに加えてBoxLayout, OverlayLayoutなどが追加されています。また、コンテナごとに省略値(デフォルト値)のレイアウト・マネージャも決まっています。例えば、パネル系のデフォルトはFlowLayoutであり、コンテント・ペインのデフォルトはBorderLayoutです。時間に余裕があれば、これまでに登場したレイアウト・マネージャの詳細をAPI仕様書で調べてください。JがつくSwingコンポーネントについても詳細をAPI仕様書で調べてみてください。
ここで紹介するサンプルは簡単なテキスト・エディタであり、ローカルのファイルシステムを参照するJFileChooserによりテキスト形式のファイルを選択して、編集結果をやはりJFileChooserで指定したファイルに保存するものです。これはSwingアプリケーションによる高度なコンポーネントの例として、JFileChooser利用のための基本的な手続きの例でもあります。是非API仕様書で更に詳細な利用方法を調べてみてください。
このサンプルの入出力は、JFileChooserで取得したテキストファイルに対するI/Oストリームを作成することで実装しています。Javaでのファイルは、ネィティブシステムの絶対パスが抽象パスとよばれるFileクラス型オブジェクトとして参照されます。JFileChooserのダイアログでファイルを選択すると、Fileクラス型オブジェクトが戻り値として返されるのです。
JavaにおけるI/Oはストリームという概念で捉えられ、オブジェクトとして扱います。例外やイベントをオブジェクトとして扱ったように、Javaでは実世界で名詞が与えられる概念は全てオブジェクトとして扱うのです。I/Oストリームは、UNIX系OSにおけるソケットに似ています。UNIX系OSでは、リモートホストをIPで識別し、ブロードキャストでMACアドレスを取得してコネクションを確立した後、目的地ホストの特定のサービスを識別するためにサービスに対応するポート番号を指定します。概念的には、IPアドレスとポート番号によって、リモートホスト間で通信用のパイプを築き、これが概念としてのソケットになります。I/Oストリームも概念的な存在であり、入出力するためのパイプに他なりません。このパイプには色々な種類があり、目的に応じて使い分けます。基本的には入出力ストリームに対して読み書きすることでデータをメモリ上に保持し、ストリームをフラッシュすることで実際のデバイスに対する入出力を行います。
Javaの入出力ストリームは文字ストリームとバイトストリームに別れ、テキストファイルは文字ストリームを使い、画像などのバイナリ・データはバイトストリームを使います。文字ストリームの最も基礎的なものはReader/Writerクラスで提供され、バイトストリームはInputStream/OutputStreamストリームで提供されます。これらのクラスを継承して派生したクラスはそれぞれ文字ストリームとバイトストリームに分けられます。
このサンプルでは、ネィティブのファイルシステムのファイルの絶対パスをJFileChooserによりFileクラス型オブジェクトとして取得し、これを文字ストリームの一つであるFileReader/FileWriterクラスでラップし、更に、効率を高めるためにBufferedReader/BufferedWriterクラスでラップしています。FileReader/FileWriterクラスは抽象パスを扱うための簡易クラスであり、システムのデフォルト文字コードを利用します。
FileReader/FileWriterは抽象パスに対する簡易クラスであり、文字コードやバッファリングサイズの指定など、文字ストリームが当然実装しておくべき機能を実装していません。これらを明示的に指定したい場合はInputStreamReader/InputStreamWriterを使います。これらのクラスは文字ストリームからバイトストリームへの橋渡しの役目を持ちますが、バッファリングをサポートしないので、入出力効率を上げるためにBufferedReader/BufferedWriterクラスを使います。
BufferedReader/BufferedWriterクラスは、1バイト単位で読み書きするReader/Writer系の入出力ストリームをインスタンス引数にとってラッピングすることで、文字/配列/行をバッファリングして文字型入力ストリームからテキストを効率良く読み込みます。
一般にデバイスに対する入出力は、CPU/メモリ間の入出力に比べて一万倍以上度遅いので、Reader/Writerによって一文字ずつその都度入出力するよりも、BufferedReader/BufferedWriterによってメモリ上に溜め込んだものを一気に書き出したほうが、ユーザに対する待たせ時間が短くなるのです。メモリ上に溜め込むことを「バッファする」、「バッファリング」と呼びます。入出力のバッファリングは、OSではスプールと呼ばれる領域に作成された待ち行列を使って実装されており、古くからよく知られたテクニックです。Javaでは入出力のバッファリングが標準クラスとして提供されているのです。
TextEditor.java
:
import java.io.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.filechooser.*; class TextEditor extends JFrame implements ActionListener { private static JFrame frame; private JTextArea textArea; private JFileChooser chooser = new JFileChooser(); public static void main(String[] args) { // 自分自身のインスタンス化 frame = new TextEditor(); // フレームのセットアップ frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE); // 終了時処理 frame.pack(); // サイズの自動セット frame.setVisible(true); // 可視化(リアライズ) } // コンストラクタ TextEditor(){ // スーパークラスのコンストラクタ呼び出し super("ファイル入出力"); // 開くボタンの作成 JButton open = new JButton("開く"); open.setActionCommand("Open"); // イベント発生時に文字列"Open"を発生 open.addActionListener(this); // 保存ボタンの作成 JButton save = new JButton("保存"); save.setActionCommand("Save"); // イベント発生時に文字列"Close"を発生 save.addActionListener(this); // テキスト・エリアの作成 textArea = new JTextArea(); textArea.setWrapStyleWord(true); textArea.setLineWrap(true); // スクロールパネルにテキスト・エリアを追加 JScrollPane scroll = new JScrollPane(textArea); scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); scroll.setPreferredSize(new Dimension(250, 250)); // パネルに部品を追加 JPanel panel = new JPanel(); panel.add(open); panel.add(save); // コンテント・ペインの取得 Container cnt = getContentPane(); // コンテント・ペインに部品を追加 cnt.add(panel, BorderLayout.NORTH); cnt.add(scroll, BorderLayout.CENTER); } // イベント処理 public void actionPerformed(ActionEvent ae){ if(ae.getActionCommand() == "Open"){ // Openボタンが押されたときの処理 int returnVal = chooser.showOpenDialog(this); // [Open File]用JFileChooserの可視化 // 戻り値ではJFileChooserが閉じられたのが、 // OKボタンによるのかキャンセルボタンによるのかを識別 try { if (returnVal == JFileChooser.APPROVE_OPTION) { // OKボタンでJFileChooserが閉じられた後の処理 File file = chooser.getSelectedFile(); // 選択されたファイルの抽象パスを取得 FileReader fr = new FileReader(file); // 文字ファイル読み込み簡易ストリーム textArea.read(fr, null); // テキスト・エリアに文字列セット frame.setTitle(file.getAbsolutePath()); // フレームのタイトル変更 fr.close(); // 読み込みバッファのクローズ } } catch(Exception ex){} } else if (ae.getActionCommand() == "Save") { // Saveボタンが押されたときの処理 int returnVal = chooser.showSaveDialog(this); try{ if (returnVal == JFileChooser.APPROVE_OPTION) { FileWriter fw = new FileWriter(chooser.getSelectedFile()); fw.write(textArea.getText()); fw.close(); } } catch(Exception ex){ ex.printStackTrace(); } } } }
コンパイル/実行結果:
>javac TextEditer.java >java TextEditer
![]() |
図:TextEditorの実行結果 |