Java Memo <prev : index : next>

モデルは中身で (2) [独自Modelの実装: CheckBoxListModel]

2002-01-26

前回は、チェックボックス付きリストを実現するにあたり、JListの実装方式を見てみました。今回は、実装に入っていきたいと思います。MorePasteでは、以下のクラスを作成しています。

  • CheckBoxListModel
  • CheckBoxCellRenderer
  • ListChecker

さっそく、話の中心であるModelのクラス、CheckBoxListModelについて見ていきましょう。前回、CheckBoxListModelでは、チェックボックスの選択状態とリスト項目の有効無効の状態を保持する、と説明しました。この2つの状態について以下では、「チェック状態」、「有効状態」と表記します。


■中身はどんな

実装の中身を見てみることにしましょう。CheckBoxListのソースは、こちらです。ソースコードにはコメントが入っていません。あしからず。

クラス定義の冒頭で、フィールド及び、コンストラクタを宣言している部分です。Vectorで宣言されている、selectedenabledが、それぞれチェック状態と有効状態を保持します。defaultSelecteddefaultEnabledは、デフォルト値を格納します。引数のないコンストラクタでは、defaultSelecteddefaultEnabledはそれぞれ、false、trueとなるようにしています。


…

public class CheckBoxListModel extends DefaultListModel {
    private Vector selected, enabled;
    private boolean defaultSelected;
    private boolean defaultEnabled;

    public CheckBoxListModel() {
        super();
        selected = new Vector();
        enabled = new Vector();

        defaultSelected = false;
        defaultEnabled = true;
    }

    …
CheckBoxListModelクラス クラス宣言及び、コンストラクタ

指定したインデックスの場所にリストの要素を挿入するメソッド、addでは、自分自身に要素を挿入する前に、チェック状態と有効状態のデフォルト値をjava.lang.Booleanでラップして格納しています。他のスレッドの割り込みでインデックスがずれたりしないように、synchronized宣言をしています。add( int, Object )は互換性を確保するために実装していて、defaultSelectedを用いて、add( int, Object, boolean )を呼んでいます。

    …

    public synchronized void add( int index, Object obj, boolean selected ) {
        this.selected.add( index, new Boolean( selected ) );
        this.enabled.add( index, new Boolean( defaultEnabled ) );
        super.add( index, obj );
    }

    public synchronized void add( int index, Object obj ) {
        add( index, obj, defaultSelected );
    }

    …
CheckBoxListModelクラス addメソッド

チェック状態を保持するselectedのgetter/setterメソッドは、次のように宣言しています。isSelectedAtメソッドでは、戻り値を返す際に、ラッパクラスBooleanのインスタンスから、booleanValueメソッドで値を取り出しています。setSelectedAtメソッドでは、対応するindexにエントリーがあるかどうか確かめてからラッパクラスに入れてセットするようにしています。

    …

    public boolean isSelectedAt( int index ) {
        Object obj = selected.get( index );
        if( obj == null ) {
        throw new NoSuchElementException();
        }
        return ( (Boolean)obj ).booleanValue();
    }

    public void setSelectedAt( int index, boolean selected ) {
        if( get( index ) == null ) {
        throw new NoSuchElementException();
        }
        this.selected.set( index, new Boolean( selected ) );
    }

    …
CheckBoxListModelクラス isSelectedAt, setSelectedAtメソッド

■状態の保持から、メソッド

なぜ、このような実装になったのか、利点、欠点をもう少し、整理してみましょう。MorePasteのバージョン1.0では、以下のようにして実装を行っています。有効状態は、明確に使用する局面が定義できているわけではありませんから、仕様上は無くても問題ない機能です。

  • リストの項目そのものは、DefaultListModelを継承して、そのまま格納する
  • チェック状態、有効状態は、それぞれVectorに格納
    • Vector内では、リスト項目と同じインデックスを持つようにする
    • 状態はbooleanなので、java.lang.Booleanでラッピングして格納する

このような実装では、リストの項目の格納は、継承したDefaultListModelの機能をそのまま用いて行います。リスト項目そのものへのアクセスは、継承したメソッドで実行できるという利点があります。リストの長さやある項目がリストに含まれているか確認するメソッドも同じように、継承したものをそのまま使用できます。

チェック状態と有効状態に関連するメソッドは、新たに作成する必要があります。リスト項目の追加や削除は、同時に2つの状態に関するデフォルト値もしくは設定値を、Vectorに格納する処理を行うように実装します。また、リスト項目のチェック状態、有効状態の取得には、別途、get/setのメソッドを用意します。

メソッドは、以下のように整理できます。CheckBoxListModelでは、DefaultListModelから継承したメソッドでそのまま使用できないものは全てオーバーライドして、実装しなおしています。このため25のメソッドを宣言していますが、本来はそこまでする必要はないでしょう。使うメソッドだけを実装すれば十分です。ただ、その場合には、実装しておらず使用できないメソッドを間違って呼び出さないように、注意する必要がありますね。

リスト項目の追加、削除 オーバーライドして実装する
項目へのアクセス、リストの長さ、項目の有無の確認 継承したメソッドをそのまま使う
チェック状態と、有効状態のget/set 新規にメソッドを実装する
コンストラクタ 継承できないので、新規にメソッドを実装する

なお、今回のような複数のVectorがインデックスでつながっている構成では、インデックスがずれるとデータが意味を持たなくなってしまいます。インデックスという細い糸が切れないようにするために、複数のVectorにまたがってインデックスが処理されるメソッドは、synchronized宣言をしておく必要があります。


■なにをやらねば

MVCのControllerのクラスであるJListは、Modelに対してListModelというインタフェースを通してアクセスします。ListModelでは以下のメソッドが宣言されていますから、それらは最低限、実装しないとコンパイルも出来ないことになります。

public int getSize() リストの長さを返す
public Object getElementAt( int index ) 指定したインデックスの値を返す
public void addListDataListener( ListDataListener l ) モデルが変更された場合に、それを通知するリスナを、リスナのリストに追加する
public void removeListDataListener( ListDataListener l ) モデルが変更された場合に、それを通知するリスナを、リスナのリストから削除する

これらは、最低限実装すればいいメソッドであり、これだけあれば十分、ということになります。しかし、通常、Modelに格納されたデータを操作する必要がありますから、それを容易にするためのメソッドを実装します。ListModelの標準の実装である、DefaultListModelでは、30以上のメソッドが用意されています。同クラスでは、内部的にデータ保持のためにVectorクラスを利用しており、ほぼ同じメソッドを宣言して、処理を委譲するような構成になっています。

プログラムの中で、コレクションクラスなどでデータを保持する、何らかのクラスが既に作られていたとします。その中身をJListなどで選択させる必要があったとしましょう。その場合、あえてデータを取り出してDefaultListModelに格納しなおすのではなく、そのクラスにListModelインタフェースを実装してしまう、という方法が考えられます。そうすれば、そのクラスをJListのsetModelメソッドで、そのままModelとして使用できます。


今回のバージョンでは、複数のVectorをインデックスで串を通すような使いかたをしていますが、1.1以降では別のやり方で実装しています。1.1での実装については、いつかお話することにして、次回は残りのCheckBoxCellRendererクラス、ListCheckerクラスの内部を見て行くことにしましょう。

おしまい

Mail to author Mail to author. Top of this page.

Versions & Requirements.
Java2 Standard Edition Version 1.3.1_02
MorePaste 1.0.1/1.1
Keywords.
MorePaste.util.CheckBoxListModel, MorePaste.util.CheckBoxCellListRenderer, MorePaste.util.ListChecker, javax.swing.JList, javax.swing.ListModel, javax.swing.DefaultListModel, MVC, Model
Related Links.
Swing Tutorial [TOC]: JList
Java House ML Search [Top]: JList, ListModel

[This page was updated: 2003-03-09 ]

 

 
Copyright © 2001-2003 Takashi KOBAYASHI. All Rights Reserved.