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