I'm working on an app which has a navigation drawer which shows a list of options taken from an SQLite table, for which I'm using a SimpleCursorAdapter as follows:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
mDrawerListView = (ListView) inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
selectItem(position);
}
});
mCursorAdapter = getList();
mDrawerListView.setAdapter(mCursorAdapter);
mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
return mDrawerListView;
}
getList()
returns a SimpleCursorAdapter, as follows:
private SimpleCursorAdapter getList()
{
Uri uri = Uri.parse("content://" + MyContentProvider.AUTHORITY + "/searches");
Cursor cursor = getActivity().getContentResolver().query(uri,
new String[]
{
SearchTable.COLUMN_ID,
SearchTable.COLUMN_SEARCH_ID,
SearchTable.COLUMN_FULL,
SearchTable.COLUMN_TYPE,
SearchTable.COLUMN_TEXT
},
null, null, null);
if (cursor == null)
{
Log.i(TAG, "FRC! Cursor is null in NavigationDrawerFragment!");
Toast.makeText(getActivity(), getString(R.string.database_error), Toast.LENGTH_SHORT).show();
}
// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] mWordListColumns =
{
SearchTable.COLUMN_TEXT,
SearchTable.COLUMN_TYPE
};
// Defines a list of View IDs that will receive the Cursor columns for each row
int[] mWordListItems = { R.id.search_full, R.id.search_type};
// layout for each of the articles in the sidebar
int layout = R.layout.search_title;
// Creates a new SimpleCursorAdapter to bind to the navigation drawer
mCursorAdapter = new SimpleCursorAdapter(
getActivity(),
layout,
cursor,
mWordListColumns,
mWordListItems,
0);
return mCursorAdapter;
}
Unfortunately, when the user refreshes the list of searches there's no change in what is shown in the navigation drawer unless the app is closed, swiped away from the task switcher, and re-launched. My content provider is notifying the app of changes in the tables and I'm watching for them as below:
class MyObserver extends ContentObserver
{
public MyObserver(Handler handler)
{
super(handler);
}
@Override
public void onChange(boolean selfChange)
{
this.onChange(selfChange, null);
}
@Override
public void onChange(boolean selfChange, Uri uri)
{
mCursorAdapter.notifyDataSetChanged();
mDrawerListView.invalidate();
Log.i(TAG,"Cursor dataset changed!");
}
}
I see the log messages for a changed dataset, so that code is actually being called. The fragment's onCreate
method contains this:
Uri searchUri = Uri.parse("content://" + MyContentProvider.AUTHORITY + "/searches");
observer = new MyObserver(new Handler());
getActivity().getContentResolver().registerContentObserver(searchUri, true, observer);
What am I missing here? Or, have I misunderstood how notifyDataSetChanged()
is supposed to work? As a workaround I was considering destroying and recreating the fragment, but since onChange()
gets called several times for multiple inserts/deletions then this might be rather a wasteful hack.
Thanks for any suggestions.
Eventually I managed to find a way to do this, which I don't think is very good but it seems to function adequately for the moment. Every other attempt to invalidate views or notify of changed datasets had no effect.
@Override
public void onPrepareOptionsMenu(Menu menu) // called when drawer opens
{
if (mDrawerLayout != null && isDrawerOpen())
{
mCursorAdapter = getList(); // create a new cursor with the latest data (see above)
mCursorAdapter.notifyDataSetChanged(); // do I even need this?
ListView listView = (ListView) getActivity().findViewById(R.id.navigation_drawer); // bind the new cursor to the listview
listView.setAdapter(mCursorAdapter);
listView.invalidateViews();
...
}
}
Please let me know if you can think of a better way!