AutoCompleteTextView with CursorLoader and SimpleCursorAdapter
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));
}
});
AutoCompletetextView with CursorLoader
This is a fine approach, according to the android developer website :
Restarting a Loader :
To discard your old data, you use restartLoader(). For example, this implementation of SearchView.OnQueryTextListener restarts the loader when the user's query changes. The loader needs to be restarted so that it can use the revised search filter to do a new query:
Migrating to CursorLoader & LoaderManager for AutoCompleteTextView with SQLiteDatabase
Unfortunately, here on SO nobody came up with a solution, yet a colleague (he doesn't have an SO account) made a pull request with the migration fixes. It works perfectly, and I decided to post the correct answer here as well.
If you're interested in how these improvements look inside the actual code, you're welcome to visit the original open source project page on GitHub: Open Weather App
This is the new adapter:
mAdapter = new SimpleCursorAdapter(this,
R.layout.dropdown_text,
null,
new String[]{CITY_COUNTRY_NAME},
new int[]{R.id.text},0);
mAdapter.setFilterQueryProvider(new FilterQueryProvider() {
@Override
public Cursor runQuery(CharSequence constraint) {
if (constraint != null) {
if (constraint.length() >= 3 && !TextUtils.isEmpty(constraint)) {
Bundle bundle = new Bundle();
String query = charArrayUpperCaser(constraint);
bundle.putString(CITY_ARGS, query);
getLoaderManager().restartLoader(0, bundle, MainActivity.this).forceLoad();
}
}
return null;
}
});
This is the onCreateLoader()
callback:
@Override
public android.content.Loader<Cursor> onCreateLoader(int id, Bundle args) {
String s = args.getString(CITY_ARGS);
WeatherCursorLoader loader = null;
if (s != null && !TextUtils.isEmpty(s)) {
loader = new WeatherCursorLoader(this, database, s);
}
return loader;
}
Custom CursorLoader
itself:
private static class WeatherCursorLoader extends CursorLoader {
private SQLiteDatabase mSQLiteDatabase;
private String mQuery;
WeatherCursorLoader(Context context, SQLiteDatabase cDatabase, String s) {
super(context);
mSQLiteDatabase = cDatabase;
mQuery = s + "%";
Log.d(TAG, "WeatherCursorLoader: " + mQuery);
}
@Override
public Cursor loadInBackground() {
return mSQLiteDatabase.query(TABLE_1, mProjection,
CITY_COUNTRY_NAME + " like ?", new String[] {mQuery},
null, null, null, "50");
}
}
AutoCompleteTextView populated by contacts using CursorLoader
Since no one else has offered an answer, let me suggest populating your array in onLoaderFinish after the cursor has been created. The Cursor Loader is essentially a query statement and not the results, which is why you can't cast from a CursorLoader to a Cursor.
Using CursorLoader to query SQLite DB and populate AutoCompleteTextView
I know this is an old question but for people who visit this page:
SimpleCursorAdapter has a new constructor:
SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags)
this cunstructor does not use UI thread. You can use it safey.
IllegalStateException - Support LoaderManager with AutocompleteTextView
OnLoadFinished seems to get called sometimes with a dead cursor - if you put a test for isClosed() on the cursor you get passed you'll find it's closed in one in (some large number) attempts.
Unfortunately the 'standard' code to put in OnLoadFinished immediately does a changeCursor() on the adapter and, well, what follows is chaos, stackdumps, pestilence, etc.
My solution isn't any prettier than your try/catch.. ignore the bogus OnLoadFinished and risk the end user getting a blank UI.
AutoCompleteTextView in android
You are likely going to have write your own Filter
and attach it via a TextWatcher
. This response has an example of regex in an AutoCompleteTextView
: Android AutoCompleteTextView with Regular Expression? and here is another regex/java example: How can I perform a partial match with java.util.regex.*?
EDIT: You will need to extend ArrayAdapter in order to override getFilter() and return your custom filter.
So you are going to have something like this:
autoCompleteTextView.setAdapter(arrayAdapter);
autoCompleteTextView.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
arrayAdapter.getFilter().filter(s);
}
});
public class RegexFilter extends Filter{
ArrayAdapter<String> mAdapter;
public RegexFilter(ArrayAdapter<String> adapter) {
mAdapter = adapter;
}
...
@Override
protected FilterResults performFiltering(CharSequence constraint) {
Pattern p = Pattern.compile(constraint);
Matcher m = p.matcher("");
List listOfMatches = new ArrayList<String>();
for (String curMonth : months) {
m.reset(curMonth);
if (m.matches || m.hitEnd()) {
listOfMatches.add(curMonth);
}
}
FilterResults results = new FilterResults();
results.values = listOfMatches;
results.count = listOfMatches.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mAdapter.addAll(results.values);
mAdapter.notifyDataSetChanged();
}
}
public class PartialArrayAdapter extends ArrayAdapter<String> {
...
RegexFilter mFilter;
@Override
public TimedSuggestionFilter getFilter() {
if(null == mFilter)
mFilter = new RegexFilter(this);
return mFilter;
}
Related Topics
Dynamically Add and Remove View to Viewpager
Using the Android Recognizerintent with a Bluetooth Headset
Android Sdk Manager Won't Open
Android P - 'Sqlite: No Such Table Error' After Copying Database from Assets
How to Access Call Log for Android
How to Pass Arraylist of Objects from One to Another Activity Using Intent in Android
How to Display Inline Images from HTML in an Android Textview
How to Clear a Notification in Android
How to Show Enable Location Dialog Like Google Maps
How to Save Sms to Inbox in Android
Datepicker: How to Popup Datepicker When Click on Edittext
How to Change Shape Color Dynamically
Create Aar File in Android Studio
How to Start Service Using Alarm Manager in Android
How to Decompile an Android .Apk File
How to Write Files to Assets Folder or Raw Folder in Android