Skip to content

Writing Your Own AutoCompleteTextView

February 8, 2010

Hey everyone,

Haven’t posted in a while but thought I’d write an example about AutoCompleteTextViews and how to implement a custom one. Much of the code is very similar in nature to that of the custom CursorAdapter which I talked about in this post:

http://thinkandroid.wordpress.com/2010/01/11/custom-cursoradapters/

However, the main difference in this example is the fact that we are going to implement the Filterable class as this is what’s going to dictate what suggestions show up during the autocomplete process. As for the layout of each entry, I implemented this instance using JAVA (hence doing it programmatically) so this example does as an example of how you can write layouts using JAVA in lieu of XML. And so, the code snippet for the adapter itself is below:

public class ContactsAutoCompleteCursorAdapter extends CursorAdapter implements Filterable {

    private TextView mName, mNumber;

    public AutoCompleteCursorAdapter(Context context, Cursor c) {
        super(context, c);
        mContent = context.getContentResolver();
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        final LinearLayout ret = new LinearLayout(context);
        final LayoutInflater inflater = LayoutInflater.from(context);
        mName = (TextView) inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
        mNumber = (TextView) inflater.inflate(android.R.layout.simple_dropdown_item_1line, parent, false);
        ret.setOrientation(LinearLayout.VERTICAL);

        LinearLayout horizontal = new LinearLayout(context);
        horizontal.setOrientation(LinearLayout.HORIZONTAL);
        
        // you can even add images to each entry of your autocomplete fields
        // this example does it programmatically using JAVA, but the XML analog is very similar
        ImageView icon = new ImageView(context);

        int nameIdx = cursor.getColumnIndexOrThrow(People.NAME);
        int numberIdx = cursor.getColumnIndex(People.NUMBER);

        String name = cursor.getString(nameIdx);
        String number = cursor.getString(numberIdx);

        mName.setText(name);
        mNumber.setText(number);

        // setting the type specifics using JAVA
        mNumber.setTextSize(16);
        mNumber.setTextColor(Color.GRAY);

        /**
         * Always add an icon, even if it is null. Keep the layout children
         * indices consistent.
         */
        Drawable image_icon  = null;
        if (carrier != CarrierInfo.NOT_CACHED && carrier != CarrierInfo.ERROR && carrier != CarrierInfo.NETWORK_ERROR) {
            image_icon = context.getResources().getDrawable(R.drawable.icon);
        }

        icon.setImageDrawable(image_icon);
        icon.setPadding(0, -2, 2, 6);

        // an example of how you can arrange your layouts programmatically
        // place the number and icon next to each other
        horizontal
                .addView(mNumber, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        horizontal.addView(icon, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

        ret.addView(mName, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        ret.addView(horizontal, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        return ret;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        int nameIdx = cursor.getColumnIndexOrThrow(People.NAME);
        int numberIdx = cursor.getColumnIndex(People.NUMBER);

        String name = cursor.getString(nameIdx);
        String number = cursor.getString(numberIdx););

        /**
         * Always add an icon, even if it is null. Keep the layout children
         * indices consistent.
         */
        Drawable image_icon  = null;
        if (carrier != CarrierInfo.NOT_CACHED && carrier != CarrierInfo.ERROR && carrier != CarrierInfo.NETWORK_ERROR) {
            image_icon = context.getResources().getDrawable(R.drawable.icon);
        }

        // notice views have already been inflated and layout has already been set so all you need to do is set the data
        ((TextView) ((LinearLayout) view).getChildAt(0)).setText(name);
        LinearLayout horizontal = (LinearLayout) ((LinearLayout) view).getChildAt(1);
        ((TextView) horizontal.getChildAt(0)).setText(number);
        ((ImageView) horizontal.getChildAt(1)).setImageDrawable(image_icon);
    }

    @Override
    public String convertToString(Cursor cursor) {
        // this method dictates what is shown when the user clicks each entry in your autocomplete list
        // in my case i want the number data to be shown
        int numCol = cursor.getColumnIndexOrThrow(People.NUMBER);
        String number = cursor.getString(numCol);
        return number;
    }

    @Override
    public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
        // this is how you query for suggestions
        // notice it is just a StringBuilder building the WHERE clause of a cursor which is the used to query for results
        if (getFilterQueryProvider() != null) { return getFilterQueryProvider().runQuery(constraint); }

        StringBuilder buffer = null;
        String[] args = null;
        if (constraint != null) {
            buffer = new StringBuilder();
            buffer.append(People.NAME + " IS NOT NULL AND " + People.NUMBER_KEY + " IS NOT NULL AND ");
            buffer.append("UPPER(");
            buffer.append(People.NAME);
            buffer.append(") GLOB ?");
            args = new String[] { constraint.toString().toUpperCase() + "*" };
        }

        return mContent.query(People.CONTENT_URI, Constants.PEOPLE_PROJECTION, buffer == null ? null : buffer
                .toString(), args, People.DEFAULT_SORT_ORDER);
    }

    private ContentResolver mContent;

}

And that’s it! So just a brief explanation (in addition to comments I put into the code at each section). Basically it works the same way as a custom CursorAdapter, but instead we implement the Filterable class as this will dynamically query our database for suggestions based off of what the user has typed into the AutoCompleteTextView. Then, we have a method called convertToString and that determines what field of the cursor gets converted to be displayed in your AutoCompleteTextView. I’m not sure what it defaults to typically, but at least in our example we want to specific between whether or not we want the contact’s name to be displayed or the contact’s phone number. When I wrote this AutoCompleteTextView, I wanted to query for the person’s contacts and allow them to quickly call that contact, and so it made more sense to display the phone number as opposed to the name.

With this you have a lot of control over not only what suggestions are displayed, but also what gets converted and how all of your information gets displayed.

So the one question remains – how does one implement this. So here’s a little code snippet on how to implement this example:

public class A extends Activity {
     
     @Override
     public void onCreate (Bundle savedInstanceState) {
            setContentView(R.layout.main);

            // R.id.auto_contact_edit is just an AutoCompleteTextView defined in the XML layout R.layout.main
            AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.auto_contact_edit);
           Cursor c = getContentResolver()
                .query(People.CONTENT_URI, Constants.PEOPLE_PROJECTION,
                        People.NAME + " IS NOT NULL AND " + People.NUMBER_KEY + " IS NOT NULL", null,
                        People.DEFAULT_SORT_ORDER);
           ContactsAutoCompleteCursorAdapter adapter = new ContactsAutoCompleteCursorAdapter(this, c);
           textView.setAdapter(adapter);
           // you can also prompt the user with a hint
           textView.setHint("type name");

           // rest of your code
     }
}

Pretty simple.

Hope this was helpful. Happy coding.

- jwei

About these ads
21 Comments leave one →
  1. February 11, 2010 12:02 pm

    Oh, just noticed this… don’t have the time to look through it atm as I need to learn some different stuff done in Android atm, but thanks for fulfilling my request ^^ hehe, it will certainly be looked at when I have the time :)

  2. May 30, 2010 5:22 am

    Thanks for the example, but I’m wondering how this example might be modified to autocomplete based on either the contact name or the contact email. For example, the native Android email client will autocomplete on either. If you have a friend like Mary Jones , the autocomplete will work when you begin typing either “ma” or “mj”. How would you accomplish this?

    • June 20, 2010 5:03 pm

      Hey Derek,

      You’re going to want to look at the runQueryOnBackgroundThread() method. This is where you take the input and customize what results get returned.

      - jwei

  3. September 27, 2010 1:32 am

    Could it be, that you have a small error in your code?!
    There is ContactsAutoCompleteCursorAdapter class, but AutoCompleteCursorAdapter-Constructor.

    Mur

  4. Vikas Patidar permalink
    October 8, 2010 6:09 am

    Hi, Thank You for this post,

    exactly what i am looking for.

    Please could you make this post more simple using XML based layout.

    I am very very new to Android and Java, and facing problems while running it..

    Thank You again..

  5. October 28, 2010 1:59 pm

    Constants.PEOPLE_PROJECTION ???

    is this the same PEOPLE_PROJECTION used in the APIDemos?

    have you done anything like this for Android 2.x?

    thanks,
    kp

  6. Christophe permalink
    November 17, 2010 6:23 am

    there is a least 4 compilation errors in this example ….

    - “CarrierInfo” does not exists
    - “Constants.PEOPLE_PROJECTION” does not exists
    - the variable “carrier” is never defined
    - and last but not least : the constructor name is wrong ….

  7. baychev permalink
    December 30, 2010 1:03 am

    Excellent idea, but have you implemented it successfully? The code is incomplete and with plenty syntax errors.

    • January 5, 2011 1:53 pm

      Hey Baychev,

      Yea I’ve implemented it successfully. The code snippet is actually from a working implementation that I did for an app I was working on. Then I just took the working class and stripped it of all irrelevant code, generalized some variable names etc and posted it.

      That probably explains any syntax errors that you might see.

      - jwei

  8. January 27, 2011 12:11 pm

    This looks really great – it will help me fix some of the comments I’ve gotten in my App…

  9. August 24, 2011 10:06 am

    Thanks for this — it gave me just enough of the CursorAdapter snippets so that I could implement this on my own.

  10. June 21, 2012 7:59 pm

    I’ve compiled this code, and got it working with Android 2.2+. Let me know if someone needs it. Just giving something back to the community.
    Thanks and credit go to the original poster.

  11. October 5, 2012 1:43 am

    I implemented a code for this too,I’m facing some problem,after I select 1 item from the autocomplete list I want the list to be shown again when the I starts typing another string

    Is it possible with your method?

    • October 5, 2012 8:53 am

      Hi Rahul,

      Yes it should be – I’m not sure what the connection is between selecting an item in the autocomplete list and having it show up again. Shouldn’t the two events be independent of one another? In other words, the autocomplete list should be ignorant of whether or not you’ve previously selected something…

      - jwei

  12. October 28, 2012 9:11 pm

    Great post! Can this be used to query an external mongodb places database? I am currently trying to implement that in an app.
    Thanks!

    • October 28, 2012 9:14 pm

      Forgot “notify via email”, reply to this post instead.

  13. Venkat. permalink
    November 22, 2012 5:00 am

    Great post! very good commentry to understand the code.. was very helpful in fixing one of my problem. thanks.!

Trackbacks

  1. uberVU - social comments
  2. Writing Your Own AutoCompleteTextView « Think Android « Internet Cafe Solution
  3. click on autocomplete entries | Mobile Techq
  4. autocomplete with database query | Android JB

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 870 other followers

%d bloggers like this: