Quantcast
Viewing all articles
Browse latest Browse all 100

Android AutocompleteTextView with Custom ArrayAdapter and SQLite

Previously, we made an example code 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.

Here's a video I shoot so you can see the final output of our code for today.




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.

Auto Complete 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!

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"?>
<LinearLayoutxmlns: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>

list_view_row.xml - helps customize the appearance of AutoCompleteTextView suggestions, you can add an ImageView or something suits your app theme or design.

<RelativeLayoutxmlns: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>

MainActivity.java - where the program will try to insert sample data, initialize some variables and will be shown when you the program runs.

packagecom.example.autocompletetextviewcustomadapter;

importandroid.os.Bundle;
importandroid.app.Activity;
importandroid.view.View;
importandroid.widget.AdapterView;
importandroid.widget.AdapterView.OnItemClickListener;
importandroid.widget.ArrayAdapter;
importandroid.widget.RelativeLayout;
importandroid.widget.TextView;

publicclassMainActivityextendsActivity {

/*
* 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
protectedvoidonCreate(BundlesavedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

try{

// instantiate database handler
databaseH =newDatabaseHandler(MainActivity.this);

// put sample data to database
insertSampleData();

// autocompletetextview is in activity_main.xml
myAutoComplete = (CustomAutoCompleteView) findViewById(R.id.myautocomplete);

myAutoComplete.setOnItemClickListener(newOnItemClickListener() {

@Override
publicvoidonItemClick(AdapterView<?>parent, Viewarg1, intpos, longid) {

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(newCustomAutoCompleteTextChangedListener(this));

// ObjectItemData has no value at first
MyObject[] ObjectItemData=newMyObject[0];

// set the custom ArrayAdapter
myAdapter =newAutocompleteCustomArrayAdapter(this, R.layout.list_view_row_item, ObjectItemData);
myAutoComplete.setAdapter(myAdapter);

} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}

publicvoidinsertSampleData(){

// CREATE
databaseH.create( newMyObject("January") );
databaseH.create( newMyObject("February") );
databaseH.create( newMyObject("March") );
databaseH.create( newMyObject("April") );
databaseH.create( newMyObject("May") );
databaseH.create( newMyObject("June") );
databaseH.create( newMyObject("July") );
databaseH.create( newMyObject("August") );
databaseH.create( newMyObject("September") );
databaseH.create( newMyObject("October") );
databaseH.create( newMyObject("November") );
databaseH.create( newMyObject("December") );
databaseH.create( newMyObject("New Caledonia this is just to make and see if the text will go down") );
databaseH.create( newMyObject("New Zealand this is just to make and see if the text will go down") );
databaseH.create( newMyObject("Papua New Guinea this is just to make and see if the text will go down") );
databaseH.create( newMyObject("COFFEE-1K") );
databaseH.create( newMyObject("coffee raw") );
databaseH.create( newMyObject("authentic COFFEE") );
databaseH.create( newMyObject("k12-coffee") );
databaseH.create( newMyObject("view coffee") );
databaseH.create( newMyObject("Indian-coffee-two") );

}
}


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.

packagecom.example.autocompletetextviewcustomadapter;

importandroid.content.Context;
importandroid.graphics.Color;
importandroid.view.LayoutInflater;
importandroid.view.View;
importandroid.view.ViewGroup;
importandroid.widget.ArrayAdapter;
importandroid.widget.TextView;

publicclassAutocompleteCustomArrayAdapterextendsArrayAdapter<MyObject> {

finalStringTAG="AutocompleteCustomArrayAdapter.java";

Context mContext;
int layoutResourceId;
MyObject data[] =null;

publicAutocompleteCustomArrayAdapter(ContextmContext, intlayoutResourceId, MyObject[] data) {

super(mContext, layoutResourceId, data);

this.layoutResourceId = layoutResourceId;
this.mContext = mContext;
this.data = data;
}

@Override
publicViewgetView(intposition, ViewconvertView, ViewGroupparent) {

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;

}
}

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.

packagecom.example.autocompletetextviewcustomadapter;

importandroid.content.Context;
importandroid.util.AttributeSet;
importandroid.widget.AutoCompleteTextView;

publicclassCustomAutoCompleteViewextendsAutoCompleteTextView {

publicCustomAutoCompleteView(Contextcontext) {
super(context);
// TODO Auto-generated constructor stub
}

publicCustomAutoCompleteView(Contextcontext, AttributeSetattrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

publicCustomAutoCompleteView(Contextcontext, AttributeSetattrs, intdefStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}

// this is how to disable AutoCompleteTextView filter
@Override
protectedvoidperformFiltering(finalCharSequencetext, finalintkeyCode) {
String filterText ="";
super.performFiltering(filterText, keyCode);
}
}


DatabaseHandler.java - where we insert sample data and query the database for autocomplete suggestions.

packagecom.example.autocompletetextviewcustomadapter;

importandroid.content.ContentValues;
importandroid.content.Context;
importandroid.database.Cursor;
importandroid.database.sqlite.SQLiteDatabase;
importandroid.database.sqlite.SQLiteOpenHelper;
importandroid.util.Log;

publicclassDatabaseHandlerextendsSQLiteOpenHelper {

// for our logs
publicstaticfinalStringTAG="DatabaseHandler.java";

// database version
privatestaticfinalintDATABASE_VERSION=5;

// database name
protectedstaticfinalStringDATABASE_NAME="NinjaDatabase2";

// table details
publicString tableName ="locations";
publicString fieldObjectId ="id";
publicString fieldObjectName ="name";

// constructor
publicDatabaseHandler(Contextcontext) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

// creating table
@Override
publicvoidonCreate(SQLiteDatabasedb) {

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
publicvoidonUpgrade(SQLiteDatabasedb, intoldVersion, intnewVersion) {

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.
*/

publicbooleancreate(MyObjectmyObj) {

boolean createSuccessful =false;

if(!checkIfExists(myObj.objectName)){

SQLiteDatabase db =this.getWritableDatabase();

ContentValues values =newContentValues();
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
publicbooleancheckIfExists(StringobjectName){

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
*/

publicMyObject[] read(StringsearchTerm) {

// 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=newMyObject[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 =newMyObject(objectName);

ObjectItemData[x] = myObject;

x++;

} while (cursor.moveToNext());
}

cursor.close();
db.close();

returnObjectItemData;

}

}

CustomAutoCompleteTextChangedListener.java - each time the user types a character, it queries the database and updates our customized ArrayAdapter.

packagecom.example.autocompletetextviewcustomadapter;

importandroid.content.Context;
importandroid.text.Editable;
importandroid.text.TextWatcher;
importandroid.util.Log;

publicclassCustomAutoCompleteTextChangedListenerimplementsTextWatcher{

publicstaticfinalStringTAG="CustomAutoCompleteTextChangedListener.java";
Context context;

publicCustomAutoCompleteTextChangedListener(Contextcontext){
this.context = context;
}

@Override
publicvoidafterTextChanged(Editables) {
// TODO Auto-generated method stub

}

@Override
publicvoidbeforeTextChanged(CharSequences, intstart, intcount,
intafter) {
// TODO Auto-generated method stub

}

@Override
publicvoidonTextChanged(CharSequenceuserInput, intstart, intbefore, intcount) {

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 =newAutocompleteCustomArrayAdapter(mainActivity, R.layout.list_view_row_item, myObjs);

mainActivity.myAutoComplete.setAdapter(mainActivity.myAdapter);

} catch (NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}

}



}


MyObject.java - used for the sample data to appear in the auto-complete suggestion.

packagecom.example.autocompletetextviewcustomadapter;

publicclassMyObject {

publicString objectName;

// constructor for adding sample data
publicMyObject(StringobjectName){

this.objectName = objectName;
}

}

Thanks for reading this Android AutocompleteTextView with SQLite and Custom ArrayAdapter example code!

Viewing all articles
Browse latest Browse all 100

Trending Articles