AndroidによくあるListViewには、様々なデータ集合を貼り付けることができます。Adapterと呼ばれるクラスが、この集合を管理します。
Adapter自体は抽象クラスで、ArrayAdapterやら、BaseAdapterやらを実際には使うことになります。そういった、Adapterの一つが、CursorAdapterです。
Cursorは、データベースにアクセスするための構造で、query()が返すiteratorのようなものです。要するに、Queryを書いて、その結果をCursorAdapterに与えることで、DBから得られる検索結果を一覧することができるというわけです。
このとき、データベースのQueryは、'_id'という、整数型のユニークな値を持ったカラムを持っていなければなりません。
が、Androidに搭載されているDBは、SQLite3。AUTOINCREMENTなデータカラムを作るには、それをPRIMARY KEYにしなければならず、他にキーを持たせたい時には使えません。
さて、どうしたものか、と思い悩んでいると、SQLite3は、頼んでもいないのに、勝手に、rowid というユニークな値を持ったカラムをこっそり作っていることが判明。
ならば、Queryに与える projection をちょいちょいといじってやれば、rowidを_idの代わりに仕立てることができるじゃないですか。以上で、余計なカラムを作らずに、_idを得ることができました。それにしても、SQLite3を使うんだから、_idじゃなくてrowidを使うように、CursorAdapterが実装されていればよかったのに……。
String[] projection = new String[] { "rowid as _id", "*" };
Cursor cursor = managedQuery(uri, projection, where, whereArgs, sort);
2012年11月4日日曜日
CursorAdapterの基本
CursorAdapterって何
Androidはちょっと大容量のデータをメモリにおくだけで不可避なOutOfMemoryエラーが発生する。
ともするとListViewなどで大量のデータを列挙をしたい場合(たとえばTwitterのタイムラインとか)だと、
メモリに保持しておく仕様だと表示する件数に限界が出てくる。列挙するデータの中に画像などが含まれたら余計にその件数は圧迫される。
で、SQLiteにデータを保持させ、そこから必要なときだけ動的にデータをSelectし、バインドさせる方法がある。
そうすることによりメモリの圧迫を抑え、膨大な件数を早い処理で画面に表示させることが出来る。
それを実現するクラスがCursorAdapterとなる。
勿論使い方は基本的に今までのAdapterと変わらない。
SimpleCursorAdapter
CursorAdapterを簡単に実装できるクラスとしてSimpleCursorAdapterがある。
SimpleCursorAdapter (Context context, int layout, Cursor c, String[] from,
int[] to)
context:コンテキスト
layout:表示するレイアウトID
cursor:SQLiteからSELECTしたCursor
from:Cursorのカラム名
to:バインドする先のViewID
が引数となっており、fromとtoのlengthは同じでなければならず、お互いが対応関係になっている。
すなわちfromで1番目に指定したカラム名の値がtoで1番目に指定したViewにバインドされる形で反映される。
これはCursorのもつ値をそのまま表示する際にはとても楽であるけど、変換や処理をかませるときには使いにくいし、
Spinnerのような二つのレイアウト(DropDown時と通常時)を持つ場合も使いにくい。
ちなみにSpinnerでそのままSimpleCursorAdapterを使う場合は、
setDropDownViewResource(resourceId)で別にレイアウトIDを指定してあげる。
コンストラクタで指定したfrom、toの関係にDropDownViewも合わせなければならないので余計に面倒。
CursorAdapter拡張
んで、SimpleCursorAdapterの大元のCursorViewを拡張する。
CursorAdapterのCursorAdapterたる部分は以下二つのメソッドのOverride。
@Override
public void bindView(View view, Context context, Cursor cursor) {
//newViewで作ったViewが引数として渡ってくるので、それに応じてバインドする
TextView tv = (TextView)view.findViewById(R.id.text);
tv.setText(cursor.getString(cursor.getColumnIndex(columnName)));
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//初めて表示されるときに呼ばれる。ここでレイアウトを定義。
View v = context.getLayoutInflater().inflate(R.layout.layout_id,
parent)
return v;
}
Spinnerの場合は同じように以下二つも拡張
getDropDownView(int position, View convertView, ViewGroup parent)
newDropDownView(Context context, Cursor cursor, ViewGroup parent)
なお、Adapeterの保持データが変更したときにそれを画面に反映させるnotifyDataSetChanged()だが、
CursorAdapterの場合はAdapterからgetCursor().requery()を呼ぶことで、内部でnotifyDataSetChanged()が呼ばれる。
やっぱり使いにくい面も
Curosrをもとに列挙させるということは、素直に作るとイレギュラーな行を途中で挿入できないということ。
だからListViewの途中でヘッダー的なレイアウトを差し込むだとかは難しい。
単純に大量のデータを列挙する、という目的には有効だが、列挙する行が増減するような複雑な処理はできないの??
Androidはちょっと大容量のデータをメモリにおくだけで不可避なOutOfMemoryエラーが発生する。
ともするとListViewなどで大量のデータを列挙をしたい場合(たとえばTwitterのタイムラインとか)だと、
メモリに保持しておく仕様だと表示する件数に限界が出てくる。列挙するデータの中に画像などが含まれたら余計にその件数は圧迫される。
で、SQLiteにデータを保持させ、そこから必要なときだけ動的にデータをSelectし、バインドさせる方法がある。
そうすることによりメモリの圧迫を抑え、膨大な件数を早い処理で画面に表示させることが出来る。
それを実現するクラスがCursorAdapterとなる。
勿論使い方は基本的に今までのAdapterと変わらない。
SimpleCursorAdapter
CursorAdapterを簡単に実装できるクラスとしてSimpleCursorAdapterがある。
SimpleCursorAdapter (Context context, int layout, Cursor c, String[] from,
int[] to)
context:コンテキスト
layout:表示するレイアウトID
cursor:SQLiteからSELECTしたCursor
from:Cursorのカラム名
to:バインドする先のViewID
が引数となっており、fromとtoのlengthは同じでなければならず、お互いが対応関係になっている。
すなわちfromで1番目に指定したカラム名の値がtoで1番目に指定したViewにバインドされる形で反映される。
これはCursorのもつ値をそのまま表示する際にはとても楽であるけど、変換や処理をかませるときには使いにくいし、
Spinnerのような二つのレイアウト(DropDown時と通常時)を持つ場合も使いにくい。
ちなみにSpinnerでそのままSimpleCursorAdapterを使う場合は、
setDropDownViewResource(resourceId)で別にレイアウトIDを指定してあげる。
コンストラクタで指定したfrom、toの関係にDropDownViewも合わせなければならないので余計に面倒。
CursorAdapter拡張
んで、SimpleCursorAdapterの大元のCursorViewを拡張する。
CursorAdapterのCursorAdapterたる部分は以下二つのメソッドのOverride。
@Override
public void bindView(View view, Context context, Cursor cursor) {
//newViewで作ったViewが引数として渡ってくるので、それに応じてバインドする
TextView tv = (TextView)view.findViewById(R.id.text);
tv.setText(cursor.getString(cursor.getColumnIndex(columnName)));
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//初めて表示されるときに呼ばれる。ここでレイアウトを定義。
View v = context.getLayoutInflater().inflate(R.layout.layout_id,
parent)
return v;
}
Spinnerの場合は同じように以下二つも拡張
getDropDownView(int position, View convertView, ViewGroup parent)
newDropDownView(Context context, Cursor cursor, ViewGroup parent)
なお、Adapeterの保持データが変更したときにそれを画面に反映させるnotifyDataSetChanged()だが、
CursorAdapterの場合はAdapterからgetCursor().requery()を呼ぶことで、内部でnotifyDataSetChanged()が呼ばれる。
やっぱり使いにくい面も
Curosrをもとに列挙させるということは、素直に作るとイレギュラーな行を途中で挿入できないということ。
だからListViewの途中でヘッダー的なレイアウトを差し込むだとかは難しい。
単純に大量のデータを列挙する、という目的には有効だが、列挙する行が増減するような複雑な処理はできないの??
登録:
投稿 (Atom)