CursorLoader と ContentProvider
備忘まで控えを作っておくことに。
とりあえず簡単な実装例、ということで ListView に出すのみ、な実装ということで。
必要なもの (というか前提)
- CursorAdapter を継承したクラス
- Activity は FragmentActivity を継承せねば、なのか
- つうことは layout は ListView 一発、ってことで良いのかな
- Activity は LoaderCallbacks interface も実装
- 手順てきには以下で良いのかどうか (Android Tips #32 CursorLoader で ContentProvider アクセスを非同期化する を参考にさせて頂いてます)
- LoaderManager で CursorLoader を読み込む
- LoaderManager.LoaderCallbacks を実装する
- CursorLoader をインスタンス化する
- 非同期処理が終わったあとの処理を記述する
なるほど。コンテンツ確認しつつ諸々確認しつつ動作確認を。
実装
基本的には
- onCreate で getSupportLoaderManager().initLoader() を呼び出す
- Activity が LoderManager.LoaderCallbacks 実装
- onCreateLoader および onLoadFinished ならびに onLoaderReset メソドの実装が必要になります
- onCreateLoader の中で CussorLoader なオブジェクトを生成
- CursorAdapter は onCreate でオブジェクト生成して ListView に setAdapter されてますね
みたいなカンジで良いのかな。直感的には CursorAdapter を継承したクラスの bindView に Cursor なオブジェクトが直接渡されるあたりがアレですね。
で、とりあえず以下な実装がでっちあがりました。とりあえずレイアウトが以下なカンジ。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ListView android:id="@+id/history_list" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout>
行なレイアウトは詳細略で。Activity の実装がざっくり以下。
public class HistoryListActivity extends FragmentActivity implements LoaderCallbacks<Cursor>{ private String TAG = "HistoryListActivity"; private CursorAdapter mAdapter; private ListView mListView; @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate"); super.onCreate(savedInstanceState); setContentView(R.layout.activity_history); mListView = (ListView)findViewById(R.id.history_list); getLoaderManager().initLoader(0, null, this); }
とりあえず、ListView な参照を確保しておいて initLoader メソドを呼び出しています。次は CursorAdapter なクラスの定義。
private class MyCursorAdapter extends CursorAdapter { public MyCursorAdapter(Context context, Cursor c, boolean autoRequery) { super(context, c, autoRequery); } @Override public void bindView(View view, Context arg1, Cursor c) { TextView tv = (TextView)view.findViewById(R.id.hoge1); tv.setText(c.getString(c.getColumnIndex(Contract.Hoge.columns.get(1)))); tv = (TextView)view.findViewById(R.id.hoge2); tv.setText(c.getString(c.getColumnIndex(Contract.Hoge.columns.get(2)))); tv = (TextView)view.findViewById(R.id.hoge3); tv.setText(c.getString(c.getColumnIndex(Contract.Hoge.columns.get(3)))); } @Override public View newView(Context arg0, Cursor arg1, ViewGroup arg2) { LayoutInflater inflater = LayoutInflater.from(arg0); View v = inflater.inflate(R.layout.row, arg2, false); return v; } }
ええと res/layout/row.xml に行毎のレイアウトが書いてある形になっています。で、最後に LoaderCallbacks なメソドの定義で以下。
@Override public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { return new CursorLoader(this, Contract.Hoge.contentUri, null, null, null, null); } @Override public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) { mAdapter = new MyCursorAdapter(this, arg1, false); mListView.setAdapter(mAdapter); } @Override public void onLoaderReset(Loader<Cursor> arg0) { mAdapter.swapCursor(null); }
onLoadFinished で CursorAdapter なインスタンスを作って setAdapter してます。なんとなく以前でっち上げた実装と微妙に違うのですが、別途このあたりの検証もしてみます。
bug
延々意味不明な例外でハマッていたのですが原因は以下でした。
tv.setText(c.getInt(c.getColumnIndex(Contract.Hoge.columns.get(2))));
これを以下に修正したら例外吐かなくなりました。
tv.setText(c.getString(c.getColumnIndex(Contract.Hoge.columns.get(2))));
例外なメセジが
android.content.res.Resources$NotFoundException: String resource ID #0x12c
みたいなソレだったのでよく分からんかったのですが、こんなの初めて見たorz
まだまだ修行が足りてないのですねorz