第4回 SWTのパネルにボタン処理を追加するには

米持幸寿
2006/2/4


米持先進技術工房 @IT分室では、テクノロジー・エバンジェリスト 米持幸寿氏が、J2EEの最新技術情報を提供します。最新技術の中身をいち早くキャッチアップしたい読者のためのコーナーです。

 前回「EclipseのVEでSWTアプリを作る」までの記事で、JVE(Java Visual Editor)を使ってパネルデザインを作る手順を簡単に紹介しました。今回はこのパネルにボタンをクリックしたときの処理を追加する方法を紹介します。なお、今回のコードはここからダウンロードできます。

挙動を決める

 前回の記事では、図1のようなパネルが表示されるBeanを作りました。

図1 前回作ったパネル

  このBeanに次の機能を付加してみます。

  1. 名前、住所のフィールドに文字を入力して「追加」ボタンをクリックすると追加される
    行が選択されている場合、その1行目の位置に挿入される
  2. エントリーを1つ選択すると、その値が上の入力フィールドに表示される
    それぞれの入力フィールドは、全選択となり、名前フィールドにフォーカスが当たる
  3. フィールドを書き換えて「更新」ボタンをクリックすると、そのエントリーの中身が書き換わる
  4. エントリーを1つ選択して「削除」ボタンをクリックすると、そのエントリーが削除される。削除された後、選択は解除(どれも選択されていない)となり、入力フィールドが消去され、名前フィールドにカーソルが置かれる。

 値の検査、さまざまなキーの取り扱いなどはまた後で考えましょう。

処理の追加方法

 JVEで作成したパネルに処理を追加する方法は、たくさんあります。例えば、次のような方法です。

  • パネルそのものにアクションロジックを追加
    JVEの標準で、最も簡単です。右クリックで追加できます。

  • サブクラス化
    これは、筆者の工夫で考え出した方法ですが、JVEで作成したクラスのサブクラスにイベント処理を持たせる方法です。JVEで作ったクラスにあまり手を加えずに処理を追加できるため、JVM担当者にほとんどJavaコードの記述をさせることなくレイアウト作業に従事させられます。JVEで作業するコードテキストに手を加えないので、JVEの動作に影響を与えることもありません。

  これらの方法についてそれぞれ解説していきます。

テーブル上のデータの管理

 今回のパネルにはテーブルが使われていますが、SWTに限らずテーブルやツリーのGUI部品は取り扱いが面倒です。

 テーブル上に表示されるデータの管理は、いくつかスタイルがあります。今回はTableクラスからTableItemオブジェクトの配列を取得してString配列をセットする方法をご紹介します。追加、変更、削除はそれぞれ次のように行います。

  • レコードの追加
    TableクラスのsetItemCount( ) メソッドによって件数をセットすると最後尾に新しい行が追加される。TableItem配列を取得して setText( ) メソッドにString配列を渡して値をセットする。途中に挿入するなら、データの移動を行う。

  • レコードの更新
    更新したい位置のTableItemに対してsetText( ) メソッドでデータを再セットします。

  • レコードの削除
    詰める必要があれば、TableItem クラスの setText( ) メソッドを使いデータの移動処理を行った後、setItemCountで少ない値をセットする。最後尾からTableItem配列が削除されます。

 リスト1に追加するためのコードを示します。

リスト1 行を追加するコード
String[] values = {textName.getText(), textAddress.getText()}; int selectionIndex = table.getSelectionIndex(); // 選択されている行位置
int prevcount = table.getItemCount();   // 現在の行数
table.setItemCount(prevcount+1);        // 行数を増やす
TableItem[] items = table.getItems();   // TableItem配列を取り出す
if(selectionIndex == -1){               // 選択されていないなら
   vector.add(values);                  // |管理用の配列最後尾に追加
   items[prevcount].setText(values);    // |追加された行のTableItemの最後尾にセット
} else {                                // 選択されているなら
   vector.add(selectionIndex, values);  // |管理用の配列の選択された位置に追加
   for(int i=selectionIndex; i<items.length; i++){ // その位置から後ろすべてを
       items[i].setText((String[])vector.get(i));? // TableItemにセットし直す
   }
}
textName.setText("");             // 名前フィールドを消去
textAddress.setText("");          // 住所フィールドを消去
textName.setFocus();              // 名前フィールドにフォーカスを当てる

 このコードでは、Vectorオブジェクト「vector」をクラス変数として持っています。これはデータ用のString配列を管理するためのものです。Tableオブジェクト「table」に対して、新しく渡されたテキスト配列のデータを選択状態にある行に追加します。選択されていない場合は、後ろに追加します。

 このコードでは、独自にVectorオブジェクトの中にString配列を管理しています。テーブル上で行が選択されている場合、その位置に配列を追加し、その後ろの値をすべてずらすために、その行から後ろのすべてのデータをセットし直す処理をしています。これで、選択された行の位置に新しいデータが挿入されます。

図2 行の挿入

 似たような考え方で、選択された行のデータを更新する、選択された行を削除するコードを以下に示します。

リスト2 データを更新する

Int selectionIndex = table.getSelectionIndex(); // 選択されている行位置
TableItem[] items = table.getItems();    // TableItem配列を取り出す
if(selectionIndex == -1){                // 選択されていないなら
    return;                              // 何もしない
} else {                                 // 選択されているなら
    items[selectionIndex].setText(values);   // その位置のTableItemにデータをセット
}

リスト3 選択された行を削除する

int[] selected = table.getSelectionIndices(); // 選択されている位置のリスト
int prevCount = vector.size();               // 現在の行数
int newCount = prevCount - selected.length;  // 削除後の行数
for(int i=prevCount-1; i>-1; i--){           // 後ろから順番に処理
  boolean toBeRemoved = false;
  for(int j=0; j<selected.length; j++){  // 選択されている位置のリストをチェック
    if(i==selected[j]) toBeRemoved = true;  // 選択されているなら削除
  }
    if(toBeRemoved) vector.remove(i);  // Vectorから削除
}
TableItem[] items = table.getItems(); ? // TableItem配列を取り出す
for(int i=selected[0]; i<newCount; i++){? // 最後に削除した位置移行すべてを
   items[i].setText((String[])vector.get(i)); // TableItemにセットし直す
}
table.setItemCount(newCount);           // 新しい行数をセットする
table.deselectAll();                    // 選択を解除する
textName.setText("");                   // 名前フィールドを消去
textAddress.setText("");                // 住所フィールドを消去
textName.setFocus();                    // 名前フィールドにフォーカスを当てる

リスト4 選択された行のデータをフィールドにコピーする

int selectionIndex = table.getSelectionIndex();  // 選択された場所
if(selectionIndex != -1){
  TableItem item = table.getItem(selectionIndex); // TableItemの取得
  textName.setText(item.getText(0));    // フィールドへ値をセット
  textAddress.setText(item.getText(1));
  textName.selectAll();                 // 全選択
  textAddress.selectAll();
  textName.setFocus();                  // フォーカスを当てる
}

ビジュアル・クラスにイベント処理を追加する

 まずは、ビジュアル・クラスに直接コードを書き込んでみましょう。作成したビジュアル・クラスは別の用途でまた使いますので、別名でコピーして、そのコードに書き込みましょう。

  まずクラス変数としてVectorオブジェクト「vector」を追加してください。このVectorオブジェクトにString配列を管理していきます。

リスト5 Vectorオブジェクトの宣言
private Vector vector = new Vector();

 ボタンがクリックされたときに呼ばれるプログラムコードは、イベント処理コードといいます。ビジュアル・エディターで作成したビジュアル・クラスに、イベント処理コードを追加するのが一番簡単な方法です。

  JVEのWYSIWYGエディター上でボタンの部品を右クリックして、「Events」−「widgetSelected」を選択することで、イベント処理コードが自動的に追加されます。そこにやることを書けばいいのです。

図3 イベント処理の追加

 どういうコードが追加されるか説明してみますが、極端な話、これは分からなくても結構です。SWTの部品は、表示されているときにクリックされると基本的には「widgetSelected」というイベントが発生します。「ユーザーがこのwidget(部品)を選択(クリック)したよ」という意味です。

 イベントというのは、ある部品から別の部品に送信される「状態変化のデータ」という感じで覚えてください。SWTの部品では、「選択された」という状態変化の情報がイベントとして送信されます。どの部品に送信されるかというと「リスナ(Listener)」という種類の部品です。リスナとは、XXXListerインターフェイスを実装(implements)するか、XXXAdapterクラスを継承したクラスで、各表示部品に搭載されているaddXXXListenerで部品に追加していきます。

 例えば、ボタンがクリックされたときには、SelectionAdapterを継承したクラスを、ボタンのオブジェクトに対してaddSelectionListener( ) メソッドで追加します。すると、アダプタークラスのwidgetSelected( )メソッドが呼ばれるわけです。

図4 イベント処理の概念

 これらをコード上で書けばいいのですが、JVEでは、そのひな型コードをコードに自動的に追加してくれます。リスト6がその自動的に追加されたコードです。

リスト6 自動的に挿入されるイベント処理コード

buttonAdd.addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
System.out.println("widgetSelected()"); // TODO Auto-generated Event stub widgetSelected()
});

 追加された直後は、コードエディタ上に自動的にジャンプするので、すぐ分かりますが、一度別の場所に移動してしまうとどこだったか分からなくなるかもしれません。その場合は、コメントの「// TODO 」の記述に従ってコード右側に青い四角で表示されていますので、これを頼りに探してください。

 この位置(System.out.printlnの行)に、テーブルにデータを追加するコードを挿入します。

 後は、同じ作業です。「更新」ボタンに更新のためのコードを、「削除」ボタンに削除のためのコードを、テーブルには、フィールドに値をコピーするコードを、それぞれの部品を右クリックして追加します。

 JVEで作成したクラスのコードに追加したコードは全部で5カ所です。コンパイルエラーがないことを確認して実行して動作を確認してください。

 次回はカスタム部品を作成し、JVE上で利用する方法について触れます。

筆者プロフィール
米持幸寿(よねもち ゆきひさ)
1987年に日本アイ・ビー・エム入社。メインフレームOS、ミドルウェアの障害対応、障害解析ソフトウェアの開発、ワークフローシステム開発、オブジェクト指向開発、Web開発などを経験。2000年より、ソフトウェアのテクノロジー・エバンジェリストとして活動中。


米持先進技術工房
テクノロジー・エバンジェリストとして活躍する米持氏が主催する、J2EEの最新技術情報を提供するWebサイト。
http://www-6.ibm.com/jp/developerworks/tips/ytech/

 



Java Solution全記事一覧



Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間