I try to populate the suggestions list with the data of a db table. However I get StaleDataException
s. It throws quite randomly, but always when I enter a character into the textview.
Here is my code:
CursorLoader extending Cristian's SimpleCursorLoader class
public class TagCursorLoader extends SimpleCursorLoader {
private String mSelection;
private TagDbLoader mDbLoader;
public TagCursorLoader(Context context, TagDbLoader dBLoader, String selection) {
super(context);
this.mDbLoader = dBLoader;
this.mSelection = selection;
}
@Override
public Cursor loadInBackground() {
return mDbLoader.fetchContainingString(mSelection);
}
}
The Loader callbacks:
public class TagCursorLoaderCallback implements LoaderCallbacks<Cursor>, CursorToStringConverter {
private Context mContext;
private TagDbLoader mdDbLoader;
private SimpleCursorAdapter mAdapter;
private String mSelection;
public TagCursorLoaderCallback(Context context, TagDbLoader dBLoader, SimpleCursorAdapter adapter) {
this.mContext = context;
this.mdDbLoader = dBLoader;
mAdapter = adapter;
mSelection = "";
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new TagCursorLoader(mContext, mdDbLoader, mSelection);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (!data.isClosed()) {
mAdapter.swapCursor(data);
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
public void setSelection(String mSelection) {
this.mSelection = mSelection;
}
@Override
public CharSequence convertToString(Cursor cursor) {
return cursor.getString(cursor.getColumnIndexOrThrow(DbConstants.Tags.KEY_TAG));
}
}
And finally when I set up the AutoCompleteTextView
:
private void initializeAutoComplete() {
mTagDbLoader = new TagDbLoader(getActivity());
mTagDbLoader.open();
mTagInput = (AutoCompleteTextView) mLayout.findViewById(R.id.autoComplete);
mTagInput.addTextChangedListener(new TextWatcherAdapter() {
@Override
public void afterTextChanged(Editable s) {
mLoaderCallback.setSelection(s.toString());
getLoaderManager().restartLoader(0, null, mLoaderCallback);
}
});
mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_1,
null, new String[] { DbConstants.Tags.KEY_TAG }, new int[] { android.R.id.text1 },
0);
mLoaderCallback = new TagCursorLoaderCallback(getActivity(), mTagDbLoader, mAdapter);
mAdapter.setCursorToStringConverter(mLoaderCallback);
mTagInput.setAdapter(mAdapter);
getLoaderManager().initLoader(0, null, mLoaderCallback);
}
After some investigation, it seems that SimpleCursorAdapter
inherits from ResourceCursorAdapter
, which inherits from CursorAdapter
. CursorAdapter
uses CursorFilter
for filtering, and this class calls changeCursor()
in its publishResults()
. changeCursor
closes the old cursor... So that's why my cursors were closed automatically.
I dropped the loaders, and changed the implementation to the code below, and it works greatly:
mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1,
mTagDbLoader.fetchAll(), new String[] { DbConstants.Tags.KEY_TAG },
new int[] { android.R.id.text1 }, 0);
mAdapter.setFilterQueryProvider(new FilterQueryProvider() {
@Override
public Cursor runQuery(CharSequence constraint) {
if (constraint == null || constraint.equals(""))
return mAdapter.getCursor();
return mTagDbLoader.fetchContainingString(constraint.toString());
}
});
mAdapter.setCursorToStringConverter(new CursorToStringConverter() {
@Override
public CharSequence convertToString(Cursor c) {
return c.getString(c.getColumnIndexOrThrow(DbConstants.Tags.KEY_TAG));
}
});