Vaadinによるボタンのイベント処理

前回では、Java用のWebアプリケーションフレームワーク「Vaadin」を用いて入力フォームをレイアウトする例を紹介した。今回は、それに対してボタンがクリックされた場合のイベント処理を追加してみる。

Vaadinのイベント処理は、AWT/Swingと同様に、コンポーネントオブジェクトに対してaddListener()メソッドを用いてリスナを登録することで行う。ボタンがクリックされた際に発生するイベントはButton.ClickEventであり、これを受け取るリスナはButton.ClickListenerインタフェースとして定義されている。したがって、まずはButton.ClickListenerをimplementsしたクラスを作成し、そのインスタンスをaddListener()でButtonオブジェクトに登録すればいいということだ。

Button.ClickListenerにはbuttonClick()メソッドがひとつ定義されている。これがクリックイベントを受け取るハンドラとなる。以下に示すSubmitButtonListenerクラスは、Button.ClickListenerインタフェースをimplementsした例である。

リスト1

public class SubmitButtonListener implements Button.ClickListener {
    private TextField nameField = null;
    private TextField messageField = null;

    public SubmitButtonListener(TextField aNameField, TextField aMessageField) {
        this.nameField = aNameField;
        this.messageField = aMessageField;
    }
    @Override
    public void buttonClick(ClickEvent event) {
        // テキストフィールドの値を取得して表示するテキストを作成
        String message = (String)this.messageField.getValue();
        String name = (String)this.nameField.getValue();
        String text = message + " by " + name;

        // イベントソースから親ウィンドウを取得
        Button source = event.getButton();
        Window parentWindow = source.getWindow();
        // 通知メッセージの表示
        parentWindow.showNotification(text);
    }
}

コンストラクタには2つのTextFieldオブジェクトを受け取る。ボタンがクリックされるとbuttonClick()メソッドが呼び出され、この2つのTextFieldに入力された値を取り出してメッセージ用の文字列を作成する。ClickEventオブジェクトに対しては、getButton()メソッドを使うことでソースとなったButtonオブジェクトを取得することができる。このButtonにgetSource()メソッドを実行すると、ボタンが配置されている親ウィンドウのWindowオブジェクトが返される。

WindowクラスのshowNotification()メソッドは、渡されたテキストをウィンドウの全面に少しの時間だけ浮かび上がらせるメソッドである。今回は、これを利用して2つのTextFieldに入力された値を表示するようにした。このSubmitButtonListenerのインスタンスを生成し、Buttonに登録するコードは次のようになる(レイアウト部分のコードは前回の記事と同様)。

リスト2

// ボタンにリスナを登録
SubmitButtonListener listener = new SubmitButtonListener(nameField, messageField);
submitButton.addListener(listener);

このプログラムの実行例を図1と図2に示す。名前とメッセージを入力して[書き込み]ボタンをクリックすると、その内容が浮かび上がるようになっている。

図1 名前とメッセージを記入してボタンをクリックする

図2 入力されたメッセージが浮かび上がる

データオブジェクトを用いたTableコンポーネントの使用例

VaadinもSwingと同様にMVCモデルを採用しているため、コンポーネントの内部で扱うデータオブジェクトは、コンポーネント本体からは独立して定義されている。その中心となるのがcom.vaadin.data.Propertyインタフェースとcom.vaadin.data.Containerインタフェースである。Propertyインタフェースは、ひとつの値からなるシンプルなデータオブジェクトを定義したもので、ラベルや入力フィールド、ボタンなどをはじめとする様々なオブジェクトの内部データとして使われる。また、これらのコンポーネントのクラス自身が、Propertyをimplementsしていることも大きな特徴である。異なるコンポーネントが共通の型のデータオブジェクトを保持することで、コンポーネント間のデータの同期などが行いやすいようになっている。

Containerの方は複数の型の値を格納することができるデータオブジェクトで、リストやテーブルなどの内部データを保持するために使用される。サブインタフェースとして整列やフィルタリングをサポートしたものがあり、これらのインタフェースの組み合わせによる、用途に応じた複数の実装クラスが用意されている。以下に、そのひとつであるIndexedContainerを利用したTableコンポーネントの使用例を紹介する。

IndexedContainerはContainer.Indexedをはじめとする4つのContainerインタフェースを実装したクラスで、インデックスを持った、整列およびフィルタリングが可能なコンテナとなっている。Containerオブジェクトに格納する値の型は、次のようにaddContainerProperty()メソッドでプロパティとして設定する。第1引数がプロパティのID、第2引数が型となるClassオブジェクトで、第3引数にはデフォルト値を指定する。

リスト3

// テーブルデータ用のコンテナを作成
IndexedContainer messageData = new IndexedContainer();
messageData.addContainerProperty("名前", String.class, "");
messageData.addContainerProperty("メッセージ", String.class, "");

テーブルを表すTableクラスは、次のようにコンストラクタの第2引数としてContainerオブジェクトを渡すことで、これをデータソースとすることができる。第1引数にはキャプションを指定する。この場合、ContainerのプロパティIDが自動的にカラムの識別子として設定される。setColumnWidth()メソッドは、カラムIDごとの幅を設定するためのメソッドである。

リスト4

// テーブルを作成してウィンドウに追加
Table messageTable = new Table("投稿されたメッセージ", messageData);
messageTable.setColumnWidth("名前", 100);
messageTable.setColumnWidth("メッセージ", 300);
messageTable.setHeight("200px");
mainWindow.addComponent(messageTable);

ページのレイアウトは図3のようになる。

図3 テーブルを配置した例

今回は、名前とメッセージを記入して[書き込み]ボタンを押したら、その内容がテーブルに追加されるようにしたい。テーブルのデータを変更するには、その内部データを保持するContainerオブジェクトを操作すればよい。そこで、ボタンがクリックされた際に呼び出されるリスナを次のように定義した。

リスト5

public class AddItemListener implements Button.ClickListener {
    private TextField nameField = null;
    private TextField messageField = null;
    private IndexedContainer messageData = null;

    public  AddItemListener(TextField aNameField, TextField aMessageField, IndexedContainer aMessageData) {
        this.nameField = aNameField;
        this.messageField = aMessageField;
        this.messageData = aMessageData;
    }

    @Override
    public void buttonClick(ClickEvent event) {
        // テキストフィールドの値を取得
        String message = (String)this.messageField.getValue();
        String name = (String)this.nameField.getValue();

        // コンテナにアイテムを追加
        Object id = this.messageData.addItem();
        this.messageData.getContainerProperty(id, "名前").setValue(name);
        this.messageData.getContainerProperty(id, "メッセージ").setValue(message);

        // テキストフィールドの値をクリア
        this.messageField.setValue("");
        this.nameField.setValue("");
    }
}

Containerにアイテムを追加するにはgetItem()メソッドを使う。このメソッドは、新たに空のアイテムを追加した上で、そのアイテムIDをObject型で返す。実際にデータを格納するには、アイテムIDとプロパティIDを指定してgetContainerProperty()を実行し、得られたPropertyオブジェクトに対してsetValue()でデータを格納すればよい。この例では、テキストフィールドに入力された名前とメッセージを取得し、それをContainerに追加している。

ボタンに対するこのリスナの登録方法は次のようになる。

リスト6

// ボタンにリスナを登録
AddItemListener listener =
    new AddItemListener(nameField, messageField, messageData);
submitButton.addListener(listener);

これで、図4のように名前とメッセージを記入してボタンを押すと、図5のようにテーブルにそれが追加されるようになった。[書き込み]ボタンをクリックするたびにデータは追加されていく(図6)。Tableコンポーネントはデフォルトで並べ替えもサポートしているため、カラム名の部分をクリックすることで図7のように昇順/降順に並べ替えることも可能となっている。

図4 名前とメッセージを記入して[書き込み]ボタンをクリック

図5 テーブルに書き込んだ内容が追加される

図6 [書き込み]ボタンをクリックするたびにデータは追加される

図7 特定のカラムによる並べ替えもサポート

Vaadinにはここで紹介した意外にもさまざまなコンポーネントやレイアウトなどが用意されている。入力値を検証するためのバリデータなどもある。デモAPIドキュメントを参照しながら様々な利用法を試してみるといいだろう。