ListAdapterについて調べてみた(3) SimpleAdapter編その2
前々回はArrayAdapterを,前回はSimpleAdapterを使ってみました。今回は前回の続きとして,SimpleAdapterのソースコードとクラスリファレンスを詳しく見て行きたいと思います。
バインドできるビューとデータ型
BaseAdapterを継承してAdapterを作る場合,getViewメソッドを実装し,getItemメソッドを使ってデータを取得して,そのデータをセルの子Viewにバインドする必要があります。また,Viewを使い回したりViewHolderを定義し,セルのViewのタグにセットしたりなど毎回毎回同じような処理を記述する必要があります。
一方で,SimpleAdapterは(List< Map< String,?>>型の)データとセルのレイアウト中の子Viewを,(どういう対応でバインドするかを引数で指定すれば)勝手にバインドしてくれます。勝手にバインドしてくれますが,全てのViewに対してバインドできるわけではありません。つまりSimpleAdapterのコンストラクタの第5引数,int型の配列の要素に入るViewのidは,全てのViewのサブクラスを指定出来るわけではありません。
- CheckBoxなどCheckableインターフェースを実装したView
- TextViewとそのサブクラス
- ImageViewとそのサブクラス
また,それぞれのViewに対応出来るデータの型にも決まりがあります。
SimpleAdapterのbindViewの一部をみてみる
今回,クラスリファレンスに加えて,Android4.3のSimpleAdapterのコードのbindViewメソッドを見てみました。
下記はbindViewメソッドの中の一つのViewと(コード中v),それに対応する一つのデータ(コード中data)を実際にバインドする箇所を抜き出した物です。また,ここでtextというのは,dataのtoString()メソッドの結果を格納している変数です。(nullな場合は,空文字に置き換わっています。)
if (v instanceof Checkable) { if (data instanceof Boolean) { ((Checkable) v).setChecked((Boolean) data); } else if (v instanceof TextView) { // Note: keep the instanceof TextView check at the bottom of these // ifs since a lot of views are TextViews (e.g. CheckBoxes). setViewText((TextView) v, text); } else { throw new IllegalStateException(v.getClass().getName() + " should be bound to a Boolean, not a " + (data == null ? "<unknown type>" : data.getClass())); } } else if (v instanceof TextView) { // Note: keep the instanceof TextView check at the bottom of these // ifs since a lot of views are TextViews (e.g. CheckBoxes). setViewText((TextView) v, text); } else if (v instanceof ImageView) { if (data instanceof Integer) { setViewImage((ImageView) v, (Integer) data); } else { setViewImage((ImageView) v, text); } } else { throw new IllegalStateException(v.getClass().getName() + " is not a " + " view that can be bounds by this SimpleAdapter"); }
CheckBoxなどCheckableインターフェースを実装したViewのバインドをみてみる
まずCheckableインターフェースを実装したViewかどうかをチェックします。
if (v instanceof Checkable) {
そして条件が真であれば,下記のようにバインドします。
if (data instanceof Boolean) { ((Checkable) v).setChecked((Boolean) data); } else if (v instanceof TextView) { // Note: keep the instanceof TextView check at the bottom of these // ifs since a lot of views are TextViews (e.g. CheckBoxes). setViewText((TextView) v, text); } else { throw new IllegalStateException(v.getClass().getName() + " should be bound to a Boolean, not a " + (data == null ? "<unknown type>" : data.getClass())); }
Boolean型オブジェクトのデータがCheckableインターフェースを実装するViewにバインドされる場合Boolean型のオブジェクトを引数にCheckable#setChecked(boolean)メソッドが実行されます。
また,バインドされるデータがBoolean型ではなくかつ,ViewがTextViewのインスタンスであれば,データのtoStringメソッドの結果を,TextView#setTextメソッドの引数としてバインドします。
それ以外の場合は,IllegalStateExceptionが投げられます。
TextViewとそのサブクラスのバインドをみてみる
} else if (v instanceof TextView) { // Note: keep the instanceof TextView check at the bottom of these // ifs since a lot of views are TextViews (e.g. CheckBoxes). setViewText((TextView) v, text);
Checkableを実装しているかどうかをチェックして結果が偽であれば,次はTextViewとそのサブクラスかどうかを確認します。もし条件が真ならsetViewTextというメソッドを用いて,バインドします。内部ではTextView#setText(CharSequence)メソッドを実行しています。
ImageViewとそのサブクラスのバインドをみてみる
} else if (v instanceof ImageView) { if (data instanceof Integer) { setViewImage((ImageView) v, (Integer) data); } else { setViewImage((ImageView) v, text); }
Checkableを実装したViewとTextViewの次は,ViewがImageViewのサブクラスであるか調べます。この場合は,データがInteger型かどうかで処理が変わります。上記のコード中で,Integer型かどうか調べて分岐先両方でsetViewImageを実行していますが,このメソッドは,第2引数がInterger型のものとString型のものがあります。
データがInteger型の場合,setViewImageメソッド内でImageView.setImageResource(int)が行われます。引数はデータです。
データがIntegerクラスでない場合,以下のような処理が行われます。
public void setViewImage(ImageView v, String value) { try { v.setImageResource(Integer.parseInt(value)); } catch (NumberFormatException nfe) { v.setImageURI(Uri.parse(value)); } }
まとめ
よく使うTextViewとImageViewがバインドは簡単にできることが分かりました。
これに加えてViewBinderを使えば複雑な処理をすることも可能です。
次回はViewBinderを使ってみたいと思います。