GUIコンポーネントの作成 (3) |
今回の例題 | ||||||||||||||
前回はアプレットでバーメータを作ってみましたが、今回はバーメータをコンポーネント化してみたいと思います。コンポーネントというのは部品のようなもので、コンテナという部品を並べる場所にぺたぺたとコンポーネントを貼っていくことができます。 前回のアプレットでは全体がひとつのバーメータでしたが、もっといろいろなものを表示させたいと思いませんでしたか。このようなときに、バーメータをコンポーネント化しておけば、他のコンポーネントと同時にコンテナに貼りつけて使用することができます。たとえば、監視システムであればバーメータ以外にもメータやトレンドグラフなども一緒に表示させたくなることがおうおうにしてあると思います。バーメータやメータもコンポーネント化しておけば、このような時でも簡単に使用することができます。 Java の AWT や Swing にはボタンやチェックボックスといったコンポーネントが備わっています。しかし、AWT や Swing
のコンポーネントではものたりない場合など、自前でコンポーネントを作ることができます。では、バーメータのコンポーネント化について説明して行くことにしましょう。 |
||||||||||||||
▲このページのトップへ戻る | ||||||||||||||
コンポーネントにはなにが必要? | ||||||||||||||
Java の GUI のコンポーネントは 2 種類あります。ひとつはヘビーウェイトコンポーネント、もうひとつがライトウェイトコンポーネントです。このコンポーネントの違いはコンポーネントが OS の機能を呼び出しているかどうかにあります。OS の機能を使用しているものがヘビーウェイトコンポーネント、Java だけで実現しているのがライトウェイトコンポーネントです。AWT の GUI コンポーネントはヘビーウェイトコンポーネントで、Swing の GUI コンポーネントは JFrame などの例外をのぞいてライトウェイトコンポーネントで実現されています。 ユーザがコンポーネントを作るときには、既存の Button や Canvas を拡張したコンポーネントを作成するか、独自のコンポーネントを作成するかのどちらかになると思います。Button や Canvas を拡張した場合は、Button などがヘビーウェイトコンポーネントなので、拡張したコンポーネントもヘビーウェイトコンポーネントになります。 独自のコンポーネントを作成する場合は、ヘビーウェイトコンポーネントとライトウェイトコンポーネントのどちらでも作ることができますが、一般には作るのが容易でかつ高速に動作するライトウェイトコンポーネントの方がおすすめです。 では、バーメータをコンポーネントにしてみましょう。ライトウェイトコンポーネントにする場合は、親クラスを java.applet.Applet から java.awt.Component に変更する必要があります。また、アプレットの時には初期化を init メソッド、start メソッドで行っていましたが、コンポーネントではこれをコンストラクタで行わなければなりません。 もうひとつだけ必要な事はコンポーネントの大きさに関することです。コンポーネントはコンテナに貼りつけられるのですが、コンテナはコンポーネントをどのように貼りつけるかをレイアウトマネージャに行わせます。JDK1.1 のレイアウトマネージャには
があります。また、Java 2 では BoxLayout や OverlayLayout が追加されています。これらのレイアウトマネージャは基本的にはコンポーネントのサイズを getPreferredSize メソッドを用いて取得して、貼りつける位置を決めて行きます。 逆にいえば、コンポーネントは getPreferredSize メソッドを書きかえて、自分がどのくらいの大きさで表示されるかを明記する必要があるわけです。このほかに、getMinimumSize メソッドも再定義して、最小の大きさを定義することが行われます。 ここまで、説明したコンポーネントの必要条件を整理してみましょう。
通常のクラスはコンストラクタで初期化を行うので、2 は自明ですが、アプレットからの変更ということで明記しておきました。 ここまでを改良したバーメータが BarMeter1.java です。BarMeter1 クラスは BarMeterApplet1 クラスに貼りつけて使用します。アプレットのデフォルトのレイアウトマネージャは FlowLayout なので、アプレットの真中に BarMeter1 が表示されます。 アプレットはここで実行できます BarMeterApplet1.html ソースファイルはこちらです BarMeter1.java BarMeterApplet1.java まずは、クラスの定義です。今までは java.applet.Applet が親クラスであったのですが、これを java.awt.Component に変更しました。
コンストラクタは特に初期化するものはないので親クラスのコンストラクタを呼び出しているだけです。
次に getPreferredSize メソッドを再定義します。getPreferredSize は実際には getMinimumSize メソッドを呼び出しているだけです。このバーメータは縦型なので幅を高さより小さくしてあります。
これだけで、コンポーネントにすることができました。以外に簡単だったのではないでしょうか。 BarMeterApplet1 ではコンポーネントを生成して、add メソッドで貼り付けを行っているだけです。
もうひとつ、つけ加えがありました。BarMeter1 はダブルバッファリングをしてちらつきを減らしているのですが、コンテナの方で背景色でクリアを行ってしまっているので、ちらついてしまいます。そこで、update メソッドを再定義して、背景色でのクリアをしないようにしておきます。
では、次にコンポーネントのプロパティを操作することについて説明したいと思います。
|
||||||||||||||
▲このページのトップへ戻る | ||||||||||||||
プロパティの設定 | ||||||||||||||
前回まではバーメータの最大/最小値などのプロパティは自分自身で設定を行っていましたが、コンポーネントにした場合は他のオブジェクトがこれらのプロパティを設定する必要があります。ただし、メンバ変数を public にして公開し、他のオブジェクトに操作させることは情報隠蔽ができなくなるなどの理由からお勧めできません。 たとえば、最大値/最小値を minimumValue、maximumValue のメンバ変数で表し、コンポーネントの使用者はこれらのメンバ変数を直接扱えたとします。あるとき、コンポーネントの作者が最大値/最小値の内部表現を最小値とレンジに変更したとしましょう。すると、maximumValue が存在しなくなってしまいますから、このコンポーネントを使用しているオブジェクトはすべて変更しなくてはなりません。 それでは、最大値/最小値を設定するメソッドを作ってみましょう。たとえば setMinimumValue メソッドと setMaximumValue メソッドとします。他のオブジェクトはこれらのメソッドを使用して最大/最小値を設定します。こうすれば、もし内部表現を変更して最小値とレンジとした場合でも setMaximumValue メソッドの実装を変更するだけで、他のオブジェクトを変更する必要はありません。 情報を隠蔽するのにはこのような理由もあるのです。 一般に Java ではプロパティを操作するために setXXXX メソッド、getXXXX を使用します。XXXX の部分にはプロパティの名前が入ります。setXXXX メソッドがプロパティを設定し、getXXXX メソッドがプロパティの値を取得するためのメソッドです。 このメソッドの命名規則は絶対というわけではありませんが、GUI だけでなく一般的な Java のコンポーネントである JavaBeans での規則となっているので統一した方が分かりやすいと思います。ライブラリのクラスにも set と get はよく用いられているので、なじみがあるのではないでしょうか。 それでは、バーメータのプロパティを設定するメソッドを定義しましょう。バーメータでは最大値/最小値がプロパティになるので、この 2種類のプロパティを操作するためのメソッドを定義します。
BarMeterApplet2 ではこの関数群を使用して最大値を 50 に、最小値を 10 に設定しています。
これを実行してみましょう BarMeterApplet2.html ソースファイルはこちらです BarMeter2.java BarMeterApplet2.java |
||||||||||||||
▲このページのトップへ戻る | ||||||||||||||
バーメータに色をつけるには、親クラスのプロパティが利用できます。java.awt.Component には色やフォントなどのプロパティがあります。それらを子クラスが利用して行けないことはないので、どんどん使ってしまいしょう。 この点の改良したものが BarMeter3 です。 ソースはここで見ることができます BarMeter3.java BarMeterApplet3.java BarMeter3 では drawBar メソッドの中で Component クラスの前景色を取得して、その色でバーを描画するようにしています。
また、ダブルバッファリング用のイメージのクリアも Component クラスの背景色を用いています (BarMeter1 クラスで記述されています)。 BarMeterApplet3 ではバーメータの前景色、背景色を設定しています。こうすることで、コンポーネントごとに色の設定を行う事ができます。
それでは実行してみましょう BarMeterApplet3.html これで、ぐっと見やすくなったのではないでしょうか。 |
||||||||||||||
▲このページのトップへ戻る | ||||||||||||||
レイアウト | ||||||||||||||
ここでコンポーネントのレイアウトについて考えてみましょう。なぜレイアウトマネージャを使用するのでしょうか。その答えの一つがさまざまなプラットフォームでアプリケーションの見た目をなるべく同一にすることにあります。レイアウトマネージャがない場合、コンポーネントの大きさや位置を細かく指定する必要があります。また、ウィンドウの大きさが変更したときにはコンポーネントの再配置を自分で行う必要があります。このような処理はとても煩雑であることはご想像のとおりです。このような煩雑な処理を肩代わりしてくれるのがレイアウトマネージャなのです。 コンポーネントはレイアウトマネージャのおかげで、大きさや位置を指定しなくても最適に配置されます。ウィンドウの大きさが変化したときには再配置が行われ、コンポーネントの大きさが変化する事があります。 たとえば BarMeterApplet4_1.html を実行してみてください。 ソースはこちらです BarMeterApplet4_1.java TestFrame.java BarMeterApplet4_1 は実行すると、フレームを生成します。このフレームは大きさを変更する事が可能なので、大きさを変えてみてください。しかし、大きさを変更してもバーメータは同じ大きさのままです。 BarMeterApplet4_1 ではレイアウトマネージャに BorderLayout を指定しています。このレイアウトマネージャは、getPreferredSize メソッドで得られたコンポーネントのサイズを考慮せずに、コンテナにぴったり入るようにコンポーネントのサイズを調整します。このため、ウィンドウの大きさが変化すると、ウィンドウと一緒にバーメータの大きさも変化するのが本来の動作であるはずです。しかし、バーメータはリサイズに対応していないため、実行したような結果になってしまったわけです。 バーメータも大きさの変更に対応できるように変更したのが BarMeter4.java です。 ソースはここで見ることができます BarMeter4.java BarMeterApplet4_2.java TestFrame.java コンポーネントの大きさが変更したときには java.awt.event.ComponentEvent が発生します。コンポーネントはこのイベントを受けとって処理を行えばいいはずです。 しかし、自分自身で起こったイベントを受けるために、自分自身にリスナーを登録して処理するのも煩雑です。このような時には processEvent メソッドを使用します。processEvent は自分自身に発生した GUI 関連のイベントすべてを処理するためのものですが、あるイベントに特化したメソッドも用意されています。ComponentEvent には processComponentEvent メソッドを利用します。 processEvent メソッドもしくは、各種イベントに特化したメソッドを使用するには、enableEvents メソッドで処理するイベントを指定しておきます。enableEvnets の引数は long ですが、ここには java.awt.AWTEvent で定義されているマスク用定数を利用できます。 BarMeter4では次のようにコンストラクタの中で ComponentEvent を処理できるように指定してます。
複数のイベントを受けるためにはマスク用定数の OR をとります。たとえば ComponentEvent と MouseEvent を処理するためには次のように表します。
サイズが変更されたときには、バッファ用のイメージもサイズを変更しなくてはならないため、リサイズされたことを表すフラグをメンバ変数に追加しました。
ComponentEvent を処理する processCompoentEvent メソッドは次のように定義しました。
ComponentEvent には大きさの変更以外にもコンポーネントの移動なども含まれているので、getID メソッドを用いてリサイズかどうかまず調べます。リサイズされていたときには、バッファ用のイメージもリサイズしなくてはならないので resizeFlag を true にしておきます。paint メソッドではフラグが立っている場合と image が null の時にバッファ用イメージ image を作成します。 また、processEvent 系のメソッドでは、最後に親クラスのメソッドを呼び出す事が必須になります。こうしないと、正しくイベントの配送が行われなくなってしまうためです。 paint メソッドではこのフラグが立っているかどうかをチェックする部分を付け加えました。こうすることで、リサイズされたときにバッファ用イメージが生成されます。その後、フラグを false に変更しておきます。
これで大きさを変更してもただしく反映されるはずです。 これを実行してみてください BarMeterApplet4_2.html フレームの大きさを変えて、正しくバーメータが表示される事を確認してみてください。 ところで、自分でコンポーネントの大きさや位置を全て決めたいという人もいるかもしれません。そんなときはレイアウトマネージャを使用しないこともできます。コンポーネントを貼りつける前に次のようにレイアウトマネージャに null を指定すればいいのです。
このとき、コンポーネントの位置と大きさの設定には setBounds メソッドや setLocation メソッド、setSize メソッドを使用します。setBounds メソッドは位置と大きさを両方設定しますが、setLocation メソッドは位置、setSize メソッドは大きさだけを設定します。
|
||||||||||||||
▲このページのトップへ戻る | ||||||||||||||
イベントを用いてコンポーネント間を結ぶ | ||||||||||||||
オブジェクト同士の依存度を高めずに関連づけを行う方法のひとつにイベントを用いることは第2回や前回で説明しましたが、もう一度おさらいしてみましょう。 BarMeterApplet5 では、指示値の生成に第1, 2回で使用した DataReader クラスを使用します。DataReader はデータを生成すると DataChangeEvent を発生するので、これをバーメータで受け取るようにすればいいわけです。 しかし、バーメータのソースを変更する必要はありません。 このように結びつくオブジェクトが変化しても、結び付けられるオブジェクトの変更を行わなくてすむのがイベントの利点です。ここではバーメータと DataReader を結び付けているのは BarMeterApplet5 です。init メソッドの中で BarMeter4 オブジェクトと DataReader オブジェクトを生成し、イベントリスナを登録することで両者を結び付けています。DataChangeListener の匿名クラスでは DataChangeEvent が発生したときに、 バーメータの setValue メソッドをコールして指示値の設定を行うだけです。
アプレットをスタートさせるには、start メソッドで setActive を true にします。
今までの例では省略していましたが、アプレットが終了するときには stop メソッドがコールされます。このときに、動作しているスレッドを終了させるなどの終了処理を行います。この例では、DataReader の stop メソッドをコールしてスレッドを終了させています。
アプレットのソースはこちらです BarMeterApplet5.java DataReader のソースはこちらです DataReader.java DataChangeEvent.java DataChangeListener.java この例では GUI のコンポーネントはバーメータしかありませんが、同じように作成できるメータやトレンドグラフも一緒に使用することも可能です。背景に工場のイメージ画像を用いて、その上にバーメータなどを配置したものなどがよく使用されていると思います。 3回にわたって、バーメータを題材にして GUI コンポーネントの作成について説明してきましたが、いかがだったでしょうか。内容的には監視システム特有のものはあまりありませんが、GUI は監視システムには欠かせないものであるため説明を加えてきました。 次回からは、もう少し監視システム特有の事項について説明して行きたいと思います。 |
||||||||||||||
▲このページのトップへ戻る | ||||||||||||||
おまけ | ||||||||||||||
本編では GUI に限ったコンポーネント化について説明を加えましたが、より一般的なコンポーネントも Java では既定されています。それが JavaBeans です。JavaBeans の仕様にのっとったコンポーネントを Bean といいますが、Bean 化することはそれほど難しいことではありません。 それでは、バーメータも Bean 化してみましょう。 Bean 化する前に行わなければならないことがあります。コンポーネントのパッケージを決めることです。コンポーネントは不特定のユーザが使用することを考慮しなくてはならないため、クラス名がぶつからないようにしなくてはなりません。パッケージを設定しておけば、同じクラス名であってもパッケージが異なれば、違うクラスとして使用することができます。 パッケージの命名規則は、ソフトウェアの作者の所属のアドレスを逆順にしたものがはじめにきて、次にそのクラスのプロジェクト名や機能を表したものにします。筆者は横河電機に所属していますが、この連載は Java Consortiumn の工業応用部会のメンバで行っていますので、Java Consortium のアドレスを採用することにしましょう。Java Consortium のアドレスは javacons.gr.jp ですので、これをひっくり返して jp.gr.javacons にします。工業応用部会は http://www.javacons.gr.jp/industry なので industry を使用して jp.gr.javacons.industry にします。デジタルエンジニアリングの講座ということで seminar をパッケージ名に続け、最後に barmeter を加えましょう。 バーメータのパッケージ jp.gr.javacons.industry.seminar.barmeter パッケージを決めたらそれに対応したディレクトリを作成して、ソースをそこに置くようにします。たとえば C:\seminar の下に作るのであれば C:\seminar\jp\gr\javacons\industry\seminar\barmeter にソースを置くようにします。 BarMeter.java は、前述した BarMeter1 から BarMeter4 までを統合して、Bean にしたものです。 では、Bean 化した BarMeter を見ていきましょう。パッケージはソースの一番先頭に指定します。
それではバーメータを Bean にしてみましょう。 Bean にするためには最低限必要なのは、次の 2つです。
しかし、BarMeter はすでにこの 2つとも定義してあります。ということは、すでに BarMeter は Bean になる資格をすでに持っていたことになります。 その他に、必須ではないのですが、Bean をシリアライズできるようにするためは次に示すことも必要です。
Bean をシリアライズできるようにすることで、Bean をファイルに保存したり、ネットワークを通じて他のホストに受け渡すこともできるようになります。 Bean のシリアライズはメンバ変数も芋づる式にシリアライズされます。このとき考慮しなくてはならないことに、すべてのメンバ変数がシリアライズできるわけではないということです。バーメータでは Image クラスが Serializable ではないため、保存することができません。このようなメンバ変数は transient というキーワードで変数を指定することで保存を行わなくなります。
ただし、Bean をロードしてきたときに、transient の変数は null になっているので、この点は気をつける必要があります。BarMeter では、すでに image が null でも動作するようにしてありますので、この点は大丈夫です。 でも、これだけではあまりにも簡単なので、BeanInfo を記述してみましょう。BeanInfo は Bean に関する情報を記述するためのクラスで、RAD などの開発環境はこれを利用して Bean の設定を行うことができます。 BeanInfo に記述する情報は次の 6種類です。
この 6 種類の情報をすべて BeanInfo に記述する必要はありません。ここでは、プロパティ、アイコンに関して BeanInfo に記述してみましょう。 BeanInfo は Bean のクラス名の後に BeanInfo を付けたクラス名となります。Bean が BarMeter なので、BeanInfo は BarMeterBeanInfo になります。 BarMeterBeanInfo はこちらで見ることができます BarMeterBeanInfo.java BeanInfo はインタフェースなのですが、最低限の部分だけ実装を行った java.beans.SimpleBeanInfo クラスがあるので、これを利用します。
まず、プロパティです。RAD などで編集可能なプロパティを並べて行きます。ここでは、最大値/最小値、バーの色、背景色の 4 つのプロパティを記述します。
次に、アイコンについて記述します。アイコンは 32×32 dot もしくは 16×16 dot の GIF などのイメージファイルを利用します。カラーとモノクロのものの両方がありますが、省略しても構いません。ここでは、barmeter32.gif と barmeter16.gif というイメージファイルを利用します。
後は、Bean と Bean に付随するクラス、BeanInfo、アイコンファイルをまとめて Jar ファイルにします。Jar にするときには meta-inf というディレクトリに manifest.mf というファイルを置く必要があります。manifest.mf には Jar に含むクラスと、そのクラスが Bean かどうかを記述します。
こうしてできた Jar ファイルはこちらでダウンロードできます barmeter.jar Jar ファイルを RAD などに登録することで、Bean を使用することが可能です。たとえば、Bean Development Kit に付随している BeanBox で BarMeter を使用してみましょう。もちろん、Visual Cafe や JBuilder、VisualAge などの市販ツールでも使用することができます。 BeanBox は 3 つのウィンドウから構成されています。左側のウィンドウが使用できる Bean の一覧を示しており、右側のウィンドウが選択している Bean のプロパティを示しています。真中のウィンドウが実際にアプレットやアプリケーションとなる部分です。図では BarMeter を選択していますが、BarMeter のプロパティが表示されているのが分かると思います。このようにして、プログラムを記述しないでもプロパティの設定を行うことができます。 また、他のコンポーネントが生成するイベントを受けることもできますが、このときはコンポーネントがこのイベントを処理するメソッドをあらかじめ用意しておかなくてはなりません。 しかし、すべてのイベントについてメソッドを記述することは大変です。 そこで、Java コンソーシアムの工業応用部会では監視システムに限定してコンポーネント間の結びつきを標準化させようとしています。この標準化案が JIM なのです。こうすれば、マルチベンダ環境でも用意にコンポーネントを結びつけることが可能になるわけです。JIM はフリーでダウンロードできますので、この際、一度試してみることもいいかもしれませんよ。 JIM は Java コンソーシアムの Web サイトからダウンロードできます。 Java コンソーシアム 工業応用部会 http://www.javacons.gr.jp/industry/index.html
|
||||||||||||||
▲このページのトップへ戻る | ||||||||||||||
ソースコードのダウンロード | ||||||||||||||
今回用いた全てのファイルはここからダウンロードできます appletsrc.zip この中には、おまけで作成した barmeter.jar も含まれています。JavaおよびJavaに関する商標は、米国Sun Microsystems社の登録商標または商標です。 |
||||||||||||||
▲このページのトップへ戻る |