sqliteデータベースの内容のリスト表示は、SimpleCursorAdapterを使うとけっこう簡単に実現する。
SimpleCursorAdapterの使い方
本家のAPIリファレンスによると、SimpleCursorAdapterのコンストラクタは以下のような引数を取る。
public SimpleCursorAdapter (Context context,
int layout,
Cursor c,
String[] from,
int[] to,
int flags)
layoutにリソースのレイアウト、cはSQLで抽出されるデータのカーソル、fromとtoでカーソルのフィールド名とレイアウトのidをそれぞれ同じ配列のインデックス位置に指定して対応づける。
flagsは、FLAG_AUTO_REQUERYとFLAG_REGISTER_CONTENT_OBSERVERが設定できると本家リファレンスに書いてあるが、まずは0を指定した。フラグの意味をちゃんと書いてあるところが見つからなかったが、
- FLAG_AUTO_REQUERY: DBが更新されたときにクエリを再発行してくれる
- FLAG_REGISTER_CONTENT_OBSERVER: 何らかの監視機構に登録してくれる
ということのようだ。特に、FLAG_AUTO_REQUERYに関しては現在「非推奨」扱いらしい。このへんはリストの更新と関わるようで詳しくは後述。
Cursorの準備
前回記事「Androidアプリでsqliteデータベースを操作する」にて作ったDBAdapterクラスに以下のCursorを返すメソッドを追加する。
private static final String PHOTO_TABLE_NAME = "photolog";
public static final String COL_ID = "id";
public static final String COL_PHOTO_URI = "photo_uri";
Cursor getURIcursor() {
final String query = "SELECT " + COL_ID + " as _id," + COL_PHOTO_URI + " FROM " + PHOTO_TABLE_NAME + ";";
Cursor c = null;
try {
c = db.rawQuery(query, null);
} catch(SQLiteException err){
Log.e(LOG_TAG, err.getMessage());
}
return c;
}
カラム名”id”(COL_ID)をわざわざ”_id”にリネームしているのは、ビルドが通っても実行時にエラーになってしまうため。Cursorに”_id”というカラムが無いといけないのは、SimpleCursorAdapterの仕様のようだ。本家のAPIリファレンスにもこのへんがはっきり書いてないのは困りもの。
レイアウトの準備
ListViewの中身のレイアウトをxmlで記述する。idとuriの2つのフィールドの幅をlayout_weightの比で指定しており、下記の記述ではidとuriが1:7で表示されるようになる。layout_widthで直接指定してしまうと、画面の幅が変わったときに格好悪い(回転させた時など)。layout_weightによって比で指定するので、layout_widthの絶対値は不要であり(無視される)、0dpを指定している。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<TextView
android:id="@+id/uri"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="7" />
</LinearLayout>
ListView構築コード
MainActivity.javaのOnCreateメソッドの中に以下を記述する。fromの指定は本来URIみたくクラス変数の参照にした方がスマートな感じだが、上記の通りCursorに_idというカラムが必須という変な仕様のため_idが直指定でちょっと格好悪い。
Cursor c = dba.getURIcursor();
String[] from = {"_id", LogDBAdapter.COL_PHOTO_URI};
int[] to = {R.id.id, R.id.uri};
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.list_item, c, from, to, 0);
ListView listView = (ListView)findViewById(R.id.listView);
listView.setAdapter(adapter);
実行結果キャプチャ
上記コードの実行結果。DBの内容がバッチリ表示されている。しかし、新しい写真を撮って戻ってきても新しいレコードがリストに追加されてくれない…。
写真追加時のリストの更新
新しい写真を撮ってもリストに反映されないのはよろしくない。そこで色々調べていたところ、こちらの記事を見つけて、なかなか奥深い問題があることを知った。要するにSimpleCursorAdapterはUIと同じスレッドで動いてしまうため、ここでDBの再クエリを行っていしまうとUIスレッドでクエリを実行することになり、UIのレスポンスが悪くなって(見かけ上の)パフォーマンスが悪くなってしまう。
SimpleCursorAdapterのコンストラクタでflags引数が更新に関わっていると上で簡単に触れた。要はUIスレッドでDB更新されることによるパフォーマンス低下ををGoogleが(ユーザーが?)嫌ったため「非推奨」という扱いになった模様。どうりでSimpleCursorAdapterでリスト更新の方法を調べても出てこないわけだ。
じゃあどうするの?の答えは「CursorLoaderを使う」ということらしい。この仕組みの場合は、DB操作は別スレッドにてバックグラウンド実行されるので、パフォーマンス低下に繋がらないと。
ただ、バックグラウンド実行には別スレッドと同期を取るためのオーバーヘッドもあるわけで、DBからほぼ固定の情報を引っ張ってきて表示するぶんには上記のSimpleCursorAdapterで十分事足りるし、SimpleCursorAdapter自体が非推奨になっているわけでもないので、状況によって使い分ければ良いんだろう。
というわけで、次回記事「CursorLoaderを使ったListView一覧表示」につづく。
コメント