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; } }); } }