Boycott A.I.
Boycott A.I.

Reputation: 18871

Android ListFragment data not updating via SimpleCursorAdapter / LoaderManager / notifyChange()

I have a ListFragment that shows the names of all the shopping lists stored in my database table.

The problem is that when I add a new shopping list row to the table, the ListFragment on the UI is not automatically updated. (The change can only be seen if I close and restart the app.)

Firstly, this is the code that is executed in my DbContentProvider class when I add a shopping list:

`

            // Insert the values into the table
            id = db.insert(SHOPPING_LISTS_META_TABLE, null, values);

            if (id > -1) {
                // Construct and return the URI of the newly inserted row.
                Uri insertedId = ContentUris.withAppendedId(CONTENT_URI_SHOPPING_LISTS_META, id);

                // Notify any observers of the change in the data set.
                Log.d(LOG_TAG, "................. notifyChange(\"" + insertedId + "\", null)");
                getContext().getContentResolver().notifyChange(insertedId, null);
                Log.d(LOG_TAG, "................. notifyChange() done");
                return insertedId;
            }
            else {
                return null;
            }

`

...and here is the LogCat output for it...

10-28 12:29:41.133: D/SQLiteOpenHelper(19401): ................. notifyChange("content://org.example.myapp.DbContentProvider/shopping_lists_meta/12", null) 10-28 12:29:41.143: D/SQLiteOpenHelper(19401): ................. notifyChange() done 10-28 12:29:41.153: D/HomeActivity(19401): Shopping list, "My Test Shopping List" created: content://org.example.myapp.DbContentProvider/shopping_lists_meta/12 10-28 12:29:41.183: D/AbsListView(19401): unregisterIRListener() is called 10-28 12:29:41.193: E/ViewRootImpl(19401): sendUserActionEvent() mView == null 10-28 12:29:41.503: D/AbsListView(19401): unregisterIRListener() is called

In LogCat, there is no output at all from my ListFragment class when I add the new shopping list row. Here is my ListFragment class...

`

public class ShoppingListNamesListFragment extends ListFragment implements LoaderManager.LoaderCallbacks {

private final static String LOG_TAG = ShoppingListNamesListFragment.class.getSimpleName(); 

// This is the Adapter being used to display the list's data
public static SimpleCursorAdapter mAdapter;

// These are the Contacts rows that we will retrieve
static final String[] PROJECTION = {DbContentProvider.KEY_ID,
    DbContentProvider.KEY_SHOPPING_LIST_NAME,
    DbContentProvider.KEY_IS_SHOPPING_LIST_SELECTED};

// This is the select criteria
static final String SELECTION = null;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Log.d(LOG_TAG, "................. onCreate()");

}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    Log.d(LOG_TAG, "................. onActivityCreated()");

    // For the cursor adapter, specify which columns go into which views
    String[] fromColumns = {DbContentProvider.KEY_SHOPPING_LIST_NAME};
    int[] toViews = {android.R.id.text1}; // The TextView in simple_list_item_1

    // Create an empty adapter we will use to display the loaded data.
    // We pass null for the cursor, then update it in onLoadFinished()
    mAdapter = new SimpleCursorAdapter(this.getActivity(), 
            android.R.layout.simple_list_item_1, null,
            fromColumns, toViews, 0);
    setListAdapter(mAdapter);

    // Prepare the loader.  Either re-connect with an existing one,
    // or start a new one.
    getLoaderManager().initLoader(0, null, this);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    Log.d(LOG_TAG, "................. onCreateView()");
    return super.onCreateView(inflater, container, savedInstanceState);
}

// Called when a new Loader needs to be created
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    Log.d(LOG_TAG, "................. onCreateLoader()");
    // Now create and return a CursorLoader that will take care of
    // creating a Cursor for the data being displayed.
    return new CursorLoader(getActivity(), DbContentProvider.CONTENT_URI_SHOPPING_LISTS_META,
            PROJECTION, SELECTION, null, null);

}

// Called when a previously created loader has finished loading
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    Log.d(LOG_TAG, "................. onLoaderFinished()");
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    mAdapter.swapCursor(data);      
}

// Called when a previously created loader is reset, making the data unavailable
@Override
public void onLoaderReset(Loader<Cursor> loader) {
    Log.d(LOG_TAG, "................. onLoaderReset()");
    // This is called when the last Cursor provided to onLoadFinished()
    // above is about to be closed.  We need to make sure we are no
    // longer using it.
    mAdapter.swapCursor(null);      
}

@Override 
public void onListItemClick(ListView l, View v, int position, long id) {
    makeToast("shopping list clicked: " + position);
    Log.d(LOG_TAG, "shopping list clicked: " + position);
}

private void makeToast(String msg) {
    Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
}

}

NB - In my DbContentProvider class, I have...

public static final Uri CONTENT_URI_SHOPPING_LISTS_META = Uri.parse("content://org.example.myapp.DbContentProvider/shopping_lists_meta"); public static final String SHOPPING_LISTS_META_TABLE = "shopping_lists_meta";

I have based my code on this Android ListFragment / LoaderManager example. And none of the similar questions to this that I have found offer a solution that fixes my problem.

I am fairly new to Android, so it could be a simple mistake I've made. But, essentially, it seems to me that when notifyChange() is called in my DbContentProvider class, my ListFragment is not being notified (or there is some other error in this area). Can anyone help?

Upvotes: 2

Views: 540

Answers (1)

Boycott A.I.
Boycott A.I.

Reputation: 18871

After spending hours and hours on this, I created a TestListActivity that hooked up to the native Contacts content provider - and that all worked/updated as it should, so I knew the issue was probably in my own content provider that I'd written.

I found the answer here. Turns out I had not called setNotificationUri(ContentResolver cr, Uri uri) on the cursor returned by the query() method of my content provider. (I'm sure this was never mentioned in the Reto Mauer book I was working from...) :/

Anyway, all sorted now! :)

Upvotes: 1

Related Questions