モデルは中身で (4) [RendererとModelをつなぐ: ListChecker]
2002-02-10
それでは、チェックボックス付きリストを構成する部品で説明し残したListCheckerクラスについて、今回は見ていきます。CheckBoxListModelで保持する、チェックボックスの選択状態とリスト項目の有効無効の2つの状態を、「チェック状態」、「有効状態」と表記するのは、前回と同様です。
■チェックはいかに
データと状態の保持、その描画の部品はそろったわけですが、最後に残るのは、どうやって操作するか、ということです。Rendererで描画はするようにしましたが、その場にはチェックボックスは存在しません。それは単に、描かれた、というだけのことです。ですから、
- いかにしてチェックボックスがクリックされたことを判断するか
- どうやって判断した結果をもとに、状態を変えるか
という処理をするか、考える必要があります。これを実現するために用意したのが、ListCheckerクラスです。ListCheckerは、実は単なるMouseListenerです。JListからマウスのイベントを拾い、クリックした場所を調べます。そこにチェックボックスがある、ということになれば、CheckBoxListModelのチェック状態を反転させるわけです。ListCheckerのソースは、こちらです。
ListCheckerは、JListとCheckBoxCellRendererを知っている必要があります。それらは、動作上欠かせないものですから、コンストラクタで渡すようにしています。
…
public class ListChecker extends MouseAdapter
{
private JList jl;
private CheckBoxCellRenderer ccr;
public ListChecker(
JList l, CheckBoxCellRenderer c ) {
jl = l;
ccr = c;
}
…
|
| ListCheckerクラス |
クラス宣言及び、コンストラクタ |
JListは、リスト項目の何番目がクリックされたのかを知るために使用されます。CheckBoxCellRendererは、クリックしたポイントがチェックボックスの中かどうか、調べさせるために使用されます。後者は、チェックボックスがどこにあるかは、描画しているコンポーネントが知っている、という理屈で、調べるためのメソッドをCheckBoxCellRendererに持たせているわけです。
クリックの回数を調べているのは、チェックボックスの中外に関わらず、ダブルクリックされていたらチェック状態を反転させるためです。「中略」としている部分では、CheckBoxListModelのリスト項目の「有効状態」を調べたり、マウスのボタンを調べたり、という条件が並んでいます。
…
public void mouseClicked( MouseEvent
e ) {
Point p
= e.getPoint();
int idx
= jl.locationToIndex( p );
CheckBoxListModel
clm = (CheckBoxListModel)jl.getModel();
try {
if( idx
> -1 && idx < jl.getModel().getSize()
…
// 中略
&&
(
ccr.isInCheckBox(
toLocationInCell( p ), clm.getElementAt( idx ).toString()
)
||
e.getClickCount() == 2
)
) {
clm.setSelectedAt(
idx, ! clm.isSelectedAt( idx ) );
jl.repaint();
}
}
catch( NoSuchElementException
exp ) {}
}
…
|
| ListCheckerクラス |
mouseClickedメソッド |
CheckBoxCellRendererのisInCheckBoxメソッドでは、チェックボックスの領域の中にあるかどうかを調べます。これにより、CheckBoxCellRendererは純粋なViewではなく、一部、Controllerの機能も持ってしまったことになります。前回、紹介した完全なソースは、こちらです。
…
public boolean isInCheckBox( Point
p, String cellText ) {
jcb.setText(
cellText );
return jcb.contains(
p );
}
|
| CheckBoxCellRendererクラス |
isInCheckBoxメソッド |
■開けごま
このように、チェックボックスつきのリストUIを実現するための部品を用意しました。これらをどのように使用するか、という話が残っています。これは、以下のような手順になります。
- 必要なインスタンス(CheckBoxCellRenderer、CheckBoxListModel、JList)を生成します。
- JListのモデルとしてCheckBoxListModelを使うように設定します。
- JListのレンダラーとして、CheckBoxCellRendererを使うように設定します。
- ListCheckerのインスタンスを生成します。その際に、JList、CheckBoxCellRendererを引数で渡します。
- ListCheckerをJListのMouseListenerに追加します。
手順でわかりにくいかもしれないのは、ListCheckerの生成でJListを引数で渡している(4)と、それにより取得したListCheckerのインスタンスを再びJListのMouseListenerに追加している(5)でしょう。この部分は、相互参照になっているため、お互いに引数に指定し合う、という処理を行うことになります。
以下は、MorePasteの[Combination]タグの実装で使用しているコードに、一部手直しを行ったものです。手順とコードの行では内容が一致していないのですが、ListCheckerのインスタンス生成とMouseListenerの追加を一度にやってしまっています。
|
CheckBoxCellRenderer ccr = new CheckBoxCellRenderer();
CheckBoxListModel clm = new CheckBoxListModel();
JList jl = new JList( clm );
jl.setCellRenderer( ccr );
jl.addMouseListener( new ListChecker(
jl, ccr ) );
|
| |
JListの初期化コードの例 |
このような手順は、JListを継承したクラスを作ってしまえば、その中に隠蔽してしまうことが出来ます。ただ、コンストラクタだけで実装できてしまうため、あえて、そのためにクラスを作るか、というところで判断が分かれるところでしょう。それで、今回は作らずに済ませてしまっています。
4回に分けて、MorePasteで実際に使用しているクラスを例に、リストのUIを独自に拡張する、という話をしてきました。流儀を理解すれば、結構、簡単にいろいろな機能を付け加えることが出来るものです。また、機能を追加する、というわかりやすい目標を立て、そのような切り口で見ていくと、モデルの理解も容易になります。
既存のクラスにModelのインタフェースを実装すれば、最小限のメソッドを追加するだけで、Controllerのクラスから使用することができるようになります。標準のModelとの間の、データの同期などを考えると、既存のクラスをそのままModelにして、置き換えてしまうほうが容易な場面が多々あるのではないでしょうか。

|