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:

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

7 Comments

  • [...] 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 [...]

  • Jonathan Foley says:

    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.

  • Dan Breslau says:

    Hi, Jonathan,

    The onClickListener that I show here doesn’t update the AutoCompleteTextView. Instead, it updates a TextView (referenced as mStateCapitalView), 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.

  • Pasa says:

    How to load data from sqlite database into AutoCompleteTextView ?

  • Dan Breslau says:

    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.

  • Steve Webb says:

    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?

  • Steve Webb says:

    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.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>