24

私は小さなTo-Doリストアプリに取り組んできました。CursorLoaderを使用して、コンテンツプロバイダーからToDolistviewを更新しました。onNewItemAdded()ユーザーがテキストビューに新しいアイテムを入力してEnterをクリックすると呼び出される関数を作成しました。以下を参照してください。

public void onNewItemAdded(String newItem) {
    ContentResolver cr = getContentResolver();

    ContentValues values = new ContentValues();
    values.put(ToDoContentProvider.KEY_TASK, newItem);

    cr.insert(ToDoContentProvider.CONTENT_URI, values);
    // getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing
}

@Override
protected void onResume() {
    super.onResume();
    //getLoaderManager().restartLoader(0, null, this); // commented for the sake of testing
}

public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    CursorLoader loader = new CursorLoader(this,
            ToDoContentProvider.CONTENT_URI, null, null, null, null);
    Log.e("GOPAL", "In the onCreateLoader");
    return loader;
}

public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    int keyTaskIndex = cursor.getColumnIndexOrThrow(ToDoContentProvider.KEY_TASK);
    Log.e("GOPAL", "In the onLoadFinished");
    todoItems.clear();
    if (cursor.moveToNext() == false) Log.e("GOPAL", "Empty Cursor");
    else {
        while (cursor.moveToNext()) {
            ToDoItem newItem = new ToDoItem(cursor.getString(keyTaskIndex));
            todoItems.add(newItem);
        }
        aa.notifyDataSetChanged(); // aa is arrayadapter used for the listview
    }
}

私が読んだところによると、コンテンツプロバイダーデータベースにデータが変更されるたびに、CursorLoaderはビューを自動的に更新します。つまりgetLoaderManager().restartLoader(0, null, this)、データに変更があるたびに暗黙的に呼び出される必要があると思いますよね?しかし、それは起こっていません。新しいアイテムを追加するたびに(アイテムはからdbに追加されますがonNewItemAdded、restartLoaderは明示的に呼び出されません)、このアクティビティを一時停止して再開します。(dbが変更されても)restartLoaderへの暗黙の呼び出しは表示されず、リストビューも新しいアイテムが追加されて更新されません。何故ですか?CursorLoaderアプリがアクティブでない場合でも、ビューを自動的に更新するにはどうすればよいですか?ありがとう :)

getContext().getContentResolver().notifyChange(insertedId, null)編集:私は私のコンテンツプロバイダーの挿入にも使用しました。

4

3 に答える 3

72

私は自分の質問に対する答えを見つけました。一般に、CursorLoaderはデータの変更を自動的に検出して、それらをロードして表示することはありません。変更についてURIを追跡する必要があります。これは、次の手順で実行できます。

  1. カーソルを使用してコンテンツリゾルバーにオブザーバーを登録する:(ContentProviderのクエリメソッドで実行)
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

  2. insert()//を使用してデータの基になるURIに変更があった場合、以下を使用して変更について 通知しdelete()ます。update(),ContentResolver

    getContext().getContentResolver().notifyChange(insertedId, null);

  3. これはオブザーバーによって受信されます。ステップ1で登録し、ContentResolver.query()これからを呼び出します。これにより、ContentProviderquery()メソッドが呼び出され、新しいカーソルがに戻りますLoaderManager。このカーソルを、新しいデータで(を使用して)ビューを更新する場所とともに渡すLoaderManager呼び出し。onLoadFinished()CursorLoaderAdapter.swapCursor()

カスタムAsyncTaskLoaderの場合:

CursorLoaderの代わりにカスタムローダーが必要になる場合があります。ここでは、カーソル以外のオブジェクトを使用して、ロードされたデータ(リストなど)を指すことができます。これでは、カーソルを介してContentResolverに通知する権限はありません。アプリケーションには、URIの変更を追跡するためのコンテンツプロバイダーがない場合もあります。このシナリオでは、BroadcastReceiverまたは明示的なContentObserverを使用して、ビューの自動更新を実現します。これは次のとおりです。

  1. AsyncTaskLoaderすべての抽象メソッドを拡張および実装するカスタムローダーを定義する必要があります。とは異なりCursorLoader、カスタムローダーはコンテンツプロバイダーを使用する場合と使用しない場合がありContentResolver.query()、このローダーが指定されている場合、そのコンストラクターはを呼び出すことができません。そのため、目的を果たすために放送受信機を使用します。
  2. 抽象クラス のBroadCastReceiverまたはContentObserverinメソッドをインスタンス化する必要があります。OnStartLoading()AsyncTaskLoader
  3. このBroadCastレシーバーは、コンテンツプロバイダーまたは任意のシステムイベント(インストールされた新しいアプリなど)からのデータ変更ブロードキャストを受信するように定義する必要がありonContentChanged()、データ変更についてローダーに通知するためにローダーのメソッドを呼び出す必要があります。ローダーは残りを自動的に実行して更新されたデータをロードし、呼び出しonLoadFinished()てビューを更新します。

詳細については、これを参照してください:http: //developer.android.com/reference/android/content/AsyncTaskLoader.html

これは明確な説明に非常に役立ちます:http ://www.androiddesignpatterns.com/2012/08/implementing-loaders.html

于 2013-03-20T16:54:44.360 に答える
3

そうですね、特定のイベントでローダーを再起動できると思います。たとえば、私の場合、TODOのアクティビティがあります。「追加」オプションをクリックすると、新しいTODOをフィードするビューを持つ新しいアクティビティが起動します。

親アクティビティのonActivityResult()で次のコードを使用しています

getLoaderManager().restartLoader(0, null, this);

それは私にとってはうまくいきます。より良いアプローチがあれば共有してください。

于 2014-06-20T13:48:03.613 に答える
2

次のように初期化中にローダーへの参照を取得します

 Loader dayWeatherLoader = getLoaderManager().initLoader(LOADER_DAY_WEATHER, null, this);

次に、ContentObserverを拡張するクラスを次のように作成します

 class DataObserver extends ContentObserver {

    public DataObserver(Handler handler) {
        super(handler);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        dayWeatherLoader.forceLoad();
    }
}

次に、コンテンツオブザーバーをonResumeライフサイクルメソッド内に次のように登録します

@Override
public void onResume() {
    super.onResume();
    getContext().getContentResolver().registerContentObserver(CONTENTPROVIDERURI,true,new DayWeatherDataObserver(new Handler()));
}

コンテンツプロバイダーの基になるデータに変更があるたびに、contentobserverのonChangeメソッドが呼び出され、ローダーにデータを再度ロードするように要求できます。

于 2017-03-09T01:23:34.893 に答える