Before this Android AutocompleteTextView with custom adapter example, we made the code on Android AutocompleteTextView with suggestions from SQLite database, that’s really fine and useful, but here’s another problem:
What if you want to customize the appearance of the drop-down suggestions?
This is where the custom ArrayAdapter will come to the scene. Using this post, you will be able to customize the look and feel of your AutocompleteTextView drop down – according to your app design theme or visual needs.
We will cover the following content:
1.0 AutocompleteTextView with Custom Adapter Video Demo
2.0 Android AutoComplete EditText Program Files
3.0 Android AutocompleteTextView with Custom Adapter Example Codes
3.1 to 3.8 Main Files Description with Codes Explained via Comments
1.0 AutocompleteTextView with Custom Adapter Video Demo
Here’s a video I shoot so you can see the final output of our code for today.
2.0 Android AutoComplete EditText Program Files
You may click on each of the files below to see the code and some insights or explanations. File # 1 and 2 were found in your res/layout/ folder, the rest is of course in the src/your.package.name
- activity_main.xml
- list_view_row.xml
- MainActivity.java
- AutocompleteCustomArrayAdapter.java
- CustomAutoCompleteView.java
- DatabaseHandler.java
- CustomAutoCompleteTextChangedListener.java
- MyObject.java
3.0 Android AutocompleteTextView with Custom Adapter Example Codes
Now here’s the exciting part. We’ll dig deeper with each main files of the program, see the codes and some explanation. Enjoy!
3.1 activity_main.xml – see how our custom AutoCompleteTextView were put in the XML layout.
The tag looks like com.example.autocompletetextviewdb.CustomAutoCompleteView because it was referenced to our CustomAutoComputeView.java file.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="" /> <com.example.autocompletetextviewcustomadapter.CustomAutoCompleteView android:id="@+id/myautocomplete" android:layout_width="fill_parent" android:layout_height="wrap_content" android:completionThreshold="1" > </com.example.autocompletetextviewcustomadapter.CustomAutoCompleteView> </LinearLayout>
3.2 list_view_row.xml – helps customize the appearance of AutoCompleteTextView suggestions, you can add an ImageView or something suits your app theme or design.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="10dp" > <TextView android:id="@+id/textViewItem" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:text="Item name here..." android:textSize="15dp" /> </RelativeLayout>
3.3 MainActivity.java – where the program will try to insert sample data, initialize some variables and will be shown when you the program runs.
package com.example.autocompletetextviewcustomadapter; import android.os.Bundle; import android.app.Activity; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.RelativeLayout; import android.widget.TextView; public class MainActivity extends Activity { /* * Change to type CustomAutoCompleteView instead of AutoCompleteTextView * since we are extending to customize the view and disable filter * The same with the XML view, type will be CustomAutoCompleteView */ CustomAutoCompleteView myAutoComplete; // adapter for auto-complete ArrayAdapter<MyObject> myAdapter; // for database operations DatabaseHandler databaseH; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try{ // instantiate database handler databaseH = new DatabaseHandler(MainActivity.this); // put sample data to database insertSampleData(); // autocompletetextview is in activity_main.xml myAutoComplete = (CustomAutoCompleteView) findViewById(R.id.myautocomplete); myAutoComplete.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View arg1, int pos, long id) { RelativeLayout rl = (RelativeLayout) arg1; TextView tv = (TextView) rl.getChildAt(0); myAutoComplete.setText(tv.getText().toString()); } }); // add the listener so it will tries to suggest while the user types myAutoComplete.addTextChangedListener(new CustomAutoCompleteTextChangedListener(this)); // ObjectItemData has no value at first MyObject[] ObjectItemData = new MyObject[0]; // set the custom ArrayAdapter myAdapter = new AutocompleteCustomArrayAdapter(this, R.layout.list_view_row_item, ObjectItemData); myAutoComplete.setAdapter(myAdapter); } catch (NullPointerException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } public void insertSampleData(){ // CREATE databaseH.create( new MyObject("January") ); databaseH.create( new MyObject("February") ); databaseH.create( new MyObject("March") ); databaseH.create( new MyObject("April") ); databaseH.create( new MyObject("May") ); databaseH.create( new MyObject("June") ); databaseH.create( new MyObject("July") ); databaseH.create( new MyObject("August") ); databaseH.create( new MyObject("September") ); databaseH.create( new MyObject("October") ); databaseH.create( new MyObject("November") ); databaseH.create( new MyObject("December") ); databaseH.create( new MyObject("New Caledonia this is just to make and see if the text will go down") ); databaseH.create( new MyObject("New Zealand this is just to make and see if the text will go down") ); databaseH.create( new MyObject("Papua New Guinea this is just to make and see if the text will go down") ); databaseH.create( new MyObject("COFFEE-1K") ); databaseH.create( new MyObject("coffee raw") ); databaseH.create( new MyObject("authentic COFFEE") ); databaseH.create( new MyObject("k12-coffee") ); databaseH.create( new MyObject("view coffee") ); databaseH.create( new MyObject("Indian-coffee-two") ); } }
3.4 AutocompleteCustomArrayAdapter.java – where you can customize the appearance of the suggestion drop-down. If your suggestions will contain a large list, I suggest using a ViewHolder pattern to make it smooth.
package com.example.autocompletetextviewcustomadapter; import android.content.Context; import android.graphics.Color; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; public class AutocompleteCustomArrayAdapter extends ArrayAdapter<MyObject> { final String TAG = "AutocompleteCustomArrayAdapter.java"; Context mContext; int layoutResourceId; MyObject data[] = null; public AutocompleteCustomArrayAdapter(Context mContext, int layoutResourceId, MyObject[] data) { super(mContext, layoutResourceId, data); this.layoutResourceId = layoutResourceId; this.mContext = mContext; this.data = data; } @Override public View getView(int position, View convertView, ViewGroup parent) { try{ /* * The convertView argument is essentially a "ScrapView" as described is Lucas post * http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/ * It will have a non-null value when ListView is asking you recycle the row layout. * So, when convertView is not null, you should simply update its contents instead of inflating a new row layout. */ if(convertView==null){ // inflate the layout LayoutInflater inflater = ((MainActivity) mContext).getLayoutInflater(); convertView = inflater.inflate(layoutResourceId, parent, false); } // object item based on the position MyObject objectItem = data[position]; // get the TextView and then set the text (item name) and tag (item ID) values TextView textViewItem = (TextView) convertView.findViewById(R.id.textViewItem); textViewItem.setText(objectItem.objectName); // in case you want to add some style, you can do something like: textViewItem.setBackgroundColor(Color.CYAN); } catch (NullPointerException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return convertView; } }
3.5 CustomAutoCompleteView.java – since we have to display everything the database gets, we have to display the AutoCompleteTextView’s default filtering function. This is also what was called in the XML layout. See activity_main.xml code above.
package com.example.autocompletetextviewcustomadapter; import android.content.Context; import android.util.AttributeSet; import android.widget.AutoCompleteTextView; public class CustomAutoCompleteView extends AutoCompleteTextView { public CustomAutoCompleteView(Context context) { super(context); // TODO Auto-generated constructor stub } public CustomAutoCompleteView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public CustomAutoCompleteView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } // this is how to disable AutoCompleteTextView filter @Override protected void performFiltering(final CharSequence text, final int keyCode) { String filterText = ""; super.performFiltering(filterText, keyCode); } }
3.6 DatabaseHandler.java – where we insert sample data and query the database for autocomplete suggestions.
package com.example.autocompletetextviewcustomadapter; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class DatabaseHandler extends SQLiteOpenHelper { // for our logs public static final String TAG = "DatabaseHandler.java"; // database version private static final int DATABASE_VERSION = 5; // database name protected static final String DATABASE_NAME = "NinjaDatabase2"; // table details public String tableName = "locations"; public String fieldObjectId = "id"; public String fieldObjectName = "name"; // constructor public DatabaseHandler(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } // creating table @Override public void onCreate(SQLiteDatabase db) { String sql = ""; sql += "CREATE TABLE " + tableName; sql += " ( "; sql += fieldObjectId + " INTEGER PRIMARY KEY AUTOINCREMENT, "; sql += fieldObjectName + " TEXT "; sql += " ) "; db.execSQL(sql); } // When upgrading the database, it will drop the current table and recreate. @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { String sql = "DROP TABLE IF EXISTS " + tableName; db.execSQL(sql); onCreate(db); } // create new record // @param myObj contains details to be added as single row. public boolean create(MyObject myObj) { boolean createSuccessful = false; if(!checkIfExists(myObj.objectName)){ SQLiteDatabase db = this.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(fieldObjectName, myObj.objectName); createSuccessful = db.insert(tableName, null, values) > 0; db.close(); if(createSuccessful){ Log.e(TAG, myObj.objectName + " created."); } } return createSuccessful; } // check if a record exists so it won't insert the next time you run this code public boolean checkIfExists(String objectName){ boolean recordExists = false; SQLiteDatabase db = this.getWritableDatabase(); Cursor cursor = db.rawQuery("SELECT " + fieldObjectId + " FROM " + tableName + " WHERE " + fieldObjectName + " = '" + objectName + "'", null); if(cursor!=null) { if(cursor.getCount()>0) { recordExists = true; } } cursor.close(); db.close(); return recordExists; } // Read records related to the search term public MyObject[] read(String searchTerm) { // select query String sql = ""; sql += "SELECT * FROM " + tableName; sql += " WHERE " + fieldObjectName + " LIKE '%" + searchTerm + "%'"; sql += " ORDER BY " + fieldObjectId + " DESC"; sql += " LIMIT 0,5"; SQLiteDatabase db = this.getWritableDatabase(); // execute the query Cursor cursor = db.rawQuery(sql, null); int recCount = cursor.getCount(); MyObject[] ObjectItemData = new MyObject[recCount]; int x = 0; // looping through all rows and adding to list if (cursor.moveToFirst()) { do { String objectName = cursor.getString(cursor.getColumnIndex(fieldObjectName)); Log.e(TAG, "objectName: " + objectName); MyObject myObject = new MyObject(objectName); ObjectItemData[x] = myObject; x++; } while (cursor.moveToNext()); } cursor.close(); db.close(); return ObjectItemData; } }
3.7 CustomAutoCompleteTextChangedListener.java – each time the user types a character, it queries the database and updates our customized ArrayAdapter.
package com.example.autocompletetextviewcustomadapter; import android.content.Context; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; public class CustomAutoCompleteTextChangedListener implements TextWatcher{ public static final String TAG = "CustomAutoCompleteTextChangedListener.java"; Context context; public CustomAutoCompleteTextChangedListener(Context context){ this.context = context; } @Override public void afterTextChanged(Editable s) { // TODO Auto-generated method stub } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub } @Override public void onTextChanged(CharSequence userInput, int start, int before, int count) { try{ // if you want to see in the logcat what the user types Log.e(TAG, "User input: " + userInput); MainActivity mainActivity = ((MainActivity) context); // update the adapater mainActivity.myAdapter.notifyDataSetChanged(); // get suggestions from the database MyObject[] myObjs = mainActivity.databaseH.read(userInput.toString()); // update the adapter mainActivity.myAdapter = new AutocompleteCustomArrayAdapter(mainActivity, R.layout.list_view_row_item, myObjs); mainActivity.myAutoComplete.setAdapter(mainActivity.myAdapter); } catch (NullPointerException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }
3.8 MyObject.java – used for the sample data to appear in the auto-complete suggestion.
package com.example.autocompletetextviewcustomadapter; public class MyObject { public String objectName; // constructor for adding sample data public MyObject(String objectName){ this.objectName = objectName; } }
I recommend reading more about Android AutocompleteTextView. Feel free to drop your questions or suggestions to improve this Android AutocompleteTextView with custom adapter example. Thanks!
21 responses to “Android AutocompleteTextView with Custom Adapter Example”
CustomAutoCompleteView – makes no sense. you can use native AutocompleteTextView for your purposes in appropriate example. and why are you calling “notifyDataSetChanged” before setting an adapter?
Hi @disqus_zJ00KJMjhq:disqus, thanks for taking the time to comment and try to improve this post, I appreciate it.
As stated above, I used CustomAutoCompleteView (extends AutoCompleteTextView) to disable the default filtering mechanism of AutoCompleteTextView.
This is based on my testing. For instance, I have 5 records in the database:
1. Cat
2. Dog
3. Snake
4. Dragon
5. Horse
If we did NOT use CustomAutoCompleteView and typed “a”, nothing will be returned and nothing will be seen on the dropdown.
If we use CustomAutoCompleteView and typed “a”, records “Cat”, “Snake” and “Dragon” will be returned for the autocomplete dropdown – since each of those words have the letter “a”.
If you have a better solution, I’m more than willing to update this post.
Regarding the “notifyDataSetChanged”, I’m calling it before setting the adapter to let AutoCompleteTextView know that the dropdown items will be changed. If I remember it correctly, I tried to call it after setting the adapter but an error was returned.
Hey,
I also had exact same doubts. In my opinion calling “notifyDataSetChanged” doesn’t really matter since you are creating a new AutocompleteCustomArrayAdapter in the next line, so that line can be removed. I also believe creating a new adapter everytime is not a correct way to do it. I tried debugging with it and adapter’s getView method was called several times for a single text change, so something isn’t right about it. I tried making a function inside adapter to change data to new supplied data(similar to what we do in Cursor Adapters) but its not working somehow.
Hi!
Thanks for this great tuto.
Little advice for your Adapter, in the getView use this ” (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)” instead of your “LayoutInflater inflater = ((MainActivity) mContext).getLayoutInflater();”
If your really want your adapter to be scalable :)
Wow, thanks for this tip @disqus_sukRkKqwwJ:disqus! I’d love to try that code. :D
for (ConversationsMember customer : memberListAll)
{
if(customer.getName().toLowerCase().startsWith(constraint.toString().toLowerCase())){
suggestions.add(customer);
}
This is the code from default filter..To populate suggestions..If you want you can modify this with this..
for (ConversationsMember customer : memberListAll)
{
if(customer.getName().toLowerCase().contains(constraint.toString().toLowerCase())){
suggestions.add(customer);
}
So,It will return data like,Cat,Snake,Dragon..If you type only ‘a’..not necessarily to be start with that letter.
hello
thanks for the good post .
your post targeting how to customize the appearance of the drop-down suggestions
but
1- can you tell me please how can I change the suggestion not to be appeared – as drop-down but appear as buttons displayed horizontally instead of certically
2- can I use 2 lists of different suggestion to drive the same autocomplete ?
hi , i have a auto complete to search a Synonyme the list item .. like syno of “January” is “ashu”. Now i will search the ashu but show the “january ” in the popup list . any sujection .
nice artitcle, it helped me, thank you.
But I found that you could not to create CustomAutoCompleteTextChangedListener and CustomAutoCompleteView. And creating adapter each time user types letter is too expensive, so maybe someone it will help, all you have to do is override getFilter:
public class SearchAdapter extends ArrayAdapter
{
//SearchData class contains only 2 fields id and name
private List mResultList;
private int mResource;
public SearchAdapter (Context context, int resource)
{
super(context, resource);
mResource = resource;
mResultList = new ArrayList();
}
@Override
public View getView (int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = LayoutInflater.from(parent.getContext()).inflate(mResource, parent, false);
}
TextView tv = (TextView) convertView.findViewById(R.id.tv_search);
tv.setText(mResultList.get(position).animalName);
return convertView;
}
@Override
public int getCount ()
{
return mResultList.size();
}
@Override
public SearchData getItem (int position)
{
return mResultList.get(position);
}
@Override
public Filter getFilter ()
{
return new Filter()
{
@Override
protected FilterResults performFiltering (CharSequence constraint)
{
FilterResults fr = new FilterResults();
if (constraint != null)
{
//here I make search from db and return result list, just replace with your own logic
List results = DBHelper.getInstance().search(constraint);
fr.count = results.size();
fr.values = results;
}
return fr;
}
@Override
protected void publishResults (CharSequence constraint, FilterResults results)
{
if (results != null && results.count > 0)
{
mResultList = (List) results.values;
notifyDataSetChanged();
} else
notifyDataSetInvalidated();
}
};
}
}
Hey Михаил Грембовский,
Thanks for sharing this code. I tried it but somehow value of ‘constraint’ in getFilter() is always coming “” for me. Any suggestions?
Hello @disqus_XbDihPiDtl:disqus, thanks for the tip. We’ll try update the code with your suggestion. :)
thanks very nice tutorial saves my whole day
Thanks for the kind words @disqus_DXwd6e6rtb:disqus! Please share our site to your friends!
Hello… everyone…I must say this tutorial did gave me hope for my college project. I just need a little bit more help. i am designing an Android Soft keyboard which do autocorrect and autocomplete on basis of user input.For now i need word suggestions in my keyboard. Can you help me in how to embed this code in my project as here you have used id of EditText but in my case, EditText can be anywhere as whole keyboard can be used anywhere in the mobile
Optionally in CustomAutoCompleteView.java,
You can override the replaceText function of its super class.
This would enable to customize the text to be set in the view after the click event
How to set text in auto complete view after clicking any item of list ?
Hi Mike Dalisay,
It’s awesome tutorial.
Thank you so much :)
Thanks for this code dude. its really helpful.
Hi there,
this post was really helpful. kudos.
this code is really helpful. but we can simply set the autoCompleteTextView.setThreshold(100000);
Then the autocomplete text will do nothing, no filter nothing. Then you can simply call showDropDown() to show the list
MyObject[] myObjs = mainActivity.databaseH.read(userInput.toString()); is generating MyObject[] required. List provided