Android: Simpler AutoCompleteTextView with SimpleCursorAdapter
Sometimes it seems like nothing brings enlightenment as swiftly as publishing the results of one’s own confusion.
Earlier today, I published a lengthy examination of the work needed to supply an AutoCompleteTextView with data using a CursorAdapter. My intentions were pure, but my code was not. Where I’d seen a need for subclassing, it was because I hadn’t caught on to some of the handler methods provided by the SimpleCursorAdapter
class.
Specifically, if we’re using a SimpleCursorAdapter, then:
- We don’t need to override the
convertToString
method, if instead we calladapter.setCursorToStringConverter
. - And there’s no need to override
runQueryOnBackgroundThread
, if instead we calladapter.setFilterQueryProvider
.
This allows a significant refactoring of the SelectState
class. Instead of the subclassed Adapter class, I’m now using anonymous inner classes to provide the cursor-to-name conversion and the filter query. The inner classes use essentially the same code as the methods that they’ve replaced.
The re-written SelectState
follows. The full example can be downloaded as a ZIP file.
(See the earlier post for a description of the sample application’s functionality.)
package org.oowb.AutoCompleteExample; import android.app.Activity; import android.database.Cursor; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.FilterQueryProvider; import android.widget.SimpleCursorAdapter; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; import android.widget.SimpleCursorAdapter.CursorToStringConverter; /** * A simple Android Activity to demonstrate: * * 1) How to use an AutoCompleteTextView with a SimpleCursorAdapter * * 2) How to access the cursor row for the user's choice, to obtain additional * data from that row when an item is selected. * * @author Dan Breslau * */ public class SelectState extends Activity { final static int[] to = new int[] { android.R.id.text1 }; final static String[] from = new String[] { "state" }; private TextView mStateCapitalView; private AutoCompleteTextView mStateNameView; private AutoCompleteDbAdapter mDbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mDbHelper = new AutoCompleteDbAdapter(this); setContentView(R.layout.selectstate); Button confirmButton = (Button) findViewById(R.id.confirm); confirmButton.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { setResult(RESULT_OK); finish(); } }); mStateCapitalView = (TextView) findViewById(R.id.state_capital); mStateNameView = (AutoCompleteTextView) findViewById(R.id.state_name); // Create a SimpleCursorAdapter for the State Name field. SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_dropdown_item_1line, null, from, to); mStateNameView.setAdapter(adapter); // Set an OnItemClickListener, to update dependent fields when // a choice is made in the AutoCompleteTextView. mStateNameView.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> listView, View view, int position, long id) { // Get the cursor, positioned to the corresponding row in the // result set Cursor cursor = (Cursor) listView.getItemAtPosition(position); // Get the state's capital from this row in the database. String capital = cursor.getString(cursor.getColumnIndexOrThrow("capital")); // Update the parent class's TextView mStateCapitalView.setText(capital); } }); // Set the CursorToStringConverter, to provide the labels for the // choices to be displayed in the AutoCompleteTextView. adapter.setCursorToStringConverter(new CursorToStringConverter() { public String convertToString(android.database.Cursor cursor) { // Get the label for this row out of the "state" column final int columnIndex = cursor.getColumnIndexOrThrow("state"); final String str = cursor.getString(columnIndex); return str; } }); // Set the FilterQueryProvider, to run queries for choices // that match the specified input. adapter.setFilterQueryProvider(new FilterQueryProvider() { public Cursor runQuery(CharSequence constraint) { // Search for states whose names begin with the specified letters. Cursor cursor = mDbHelper.getMatchingStates( (constraint != null ? constraint.toString() : null)); return cursor; } }); } }Android, AutoCompleteTextView, CursorAdapter, SimpleCursorAdapter
[…] This post was mentioned on Twitter by Yamane Toshiaki, Dan Breslau. Dan Breslau said: Nothing brings enlightenment as swiftly as publishing the results of one’s own confusion. Simpler #androiddev ACTV http://bit.ly/actvsca […]
At least on 2.2 you don’t need the onClickListener as the AutoCompleteTextView handles this itself. In fact, if you add one as you have here it operates strangely in that when call setText, this again calls the query and it again displays the drop-down.
Hi, Jonathan,
The
onClickListener
that I show here doesn’t update the AutoCompleteTextView. Instead, it updates a TextView (referenced asmStateCapitalView
), whose value needs to change as the value in the AutoCompleteTextView changes. You’re right that this would be redundant if it were updating the AutoCompleteTextView itself.How to load data from sqlite database into AutoCompleteTextView ?
Hi, Pasa,
I’m sorry, but I don’t understand your question. The example that’s shown here does load data from an sqlite database into an AutoCompleteTextView. Have a look at the method
setCursorToStringConverter
, which is called to create the text string that is displayed in each row of the AutoCompleteTextView. You probably need to implement a similar method in your application.Nice demo thanks. My only question is that I don’t see any cursor management going on in the code. How/When are the cursors closed?
It’s ok I can now see in the Javadoc that the cursor returned from the runQuery is used to update the managed cursor in the adaptor and the old one is closed.
Thank you very much, Dan. Great post, clear code, perfec explanations. This just cured a big headache.
I’ve never cleared my confusion in acceptable amount of time without your post, thanks for share
I use adapter extend cursor adapter. I overrided runQueryOnBackgroundThread but i got error android.database.StaleDataException: Access closed cursor. Please help me out.
Hi, Jul,
I think the best way for you to get an answer is to post the question on http://www.stackoverflow.com. When you post, include the relevant code to let readers identify the problem.
Hallelujah thank you sir you saved my life
[…] a SQLite database I would like to query. I want to target Android 2.2 through ICS. I came across this article on how to do this, but it uses deprecated code (does not query asynchronously, but on the […]