Question

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.

Was it helpful?

Solution

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!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top