【Android】Adapterをforeachで使えるようにする
Adapterってあるじゃないですか。ListViewとか、ListViewとか、ListViewで使うあれです。あれ、foreachで回したくなりません?
いや確かに、Adapterを作る以上、ほとんどの場合は何らかのIterableなもの(例外はCursorぐらい?あれもiteratorパターンではあるけど)をコンストラクタで渡すのであって、それをメンバ変数なりなんなりで保持しておき、foreachしたい時はそれを使えばいいでしょう。
ただ、ArrayAdapter<T>がうっかりaddを実装しており、adapterとiterableなメンバの同期を取るのってめんどくさくない?adapterに全部あるんだからそっから全部とればいいじゃん、とか、そう言うことを度々思うので、取れるようにします。
Iterable<T>とIterator<E>を実装する
今更感があるんですが、foreach構文(Javaは拡張for文って呼ぶんだっけ)を適用するためにはIteratable<T>を実装する必要があります。このインターフェースで定められているメソッドはiteratorメソッドのみです。で、これは何をする必要があるのかと言うと、なんでもいいからIterator<E>を返してやるだけです。
Iterator<E>のメソッドはhasNextとnextの二つが必須になっています。removeは任意のオペレーションとのことなので、まぁ実装したければどうぞって感じです。
つまり、「次の要素があるか?」と「次の要素は何か?」の二つがわかればIteratorパターンは実装できるわけですね。これをAdapterの観点から考えてみましょう。
AdapterのメソッドからIteratorパターンを作る
Adapterの親子関係は若干複雑で、直接Adapterを実装しているのはListAdapterとSpinnerAdapterだけです。
このAdapterを実装した二つのインターフェースを両方実装しているのがBaseAdapterという抽象クラスになっています。ArrayAdapter<T>などの具象クラスは、このBaseAdapterを継承しています。
だからリファレンスではこういう風に表現されているわけです。
java.lang.Object ?android.widget.BaseAdapter ?android.widget.ArrayAdapter<T>
とは言え、辿っていけばちゃんとすべての具象クラスがAdapterインターフェースを実装していることになります。そしてAdapterにはItartorパターンを作る上で必要そうなgetCountメソッドとgetItemメソッドが規定されています。
コード
早速作ってみましょう。
[2014/10/15修正]なぜかremoveが実装されていなかったので追加しておきました。
public final class AdapertToIterable<T> implements Iterable<T>, Iterator<T> { private int _position; private int _maxCount; private Adapter _adapter; public AdapertToIterable(Adapter adapter) { _adapter = adapter; _maxCount = !adapter.isEmpty() ? adapter.getCount() : 0; } @Override public Iterator<T> iterator() { return this; } @Override public boolean hasNext() { return _position < _maxCount; } @Override public T next() { return (T) _adapter.getItem(_position++); } @Override public void remove() { throw new UnSupportedOperationException(); } }
超簡単ですね。これでIterableなAdapterを手に入れることが出来ました。
まとめ
特にまとめるほどのあれもないです。