Using AutoCompleteTextView, you can show help text as drop down while user typing text into the text field. If for a particular field, there are only few possible values in your application, you can load the data from source and add it to adapter of AutoCompleteTextView as doing so doesn’t require much resource.
But if there is a plenty of data that need to be searched against, it is not good idea to keep entire data in memory. The best way to handle this scenario is searching and getting data from database as user enters text and show auto complete suggestion.
In this post, you can learn dynamically populating AutoCompleteTextView suggestions drop down by reading data from database using room and SQLite databse.
To learn more about AutoCompleteTextView, you can read AutoCompleteTextView tutorial, populating auto complete dropdown with data from web services, and AutoCompleteTextView custom layout and adapter.
If you are new to Room persistency framework, you can see Android Room and steps to use Room, Android Room example, and database migration with Room.
Since we are using room to read and write data to SQLite databse, we need to add below room libraries to gradle build file.
dependencies {
. . .
implementation 'android.arch.persistence.room:runtime:1.0.0-beta2'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-beta2'
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/store"
style="?android:attr/dropDownItemStyle"
android:layout_width="200dp"
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_marginLeft="8dp"
android:ellipsize="marquee"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/coupon"
style="?android:attr/dropDownItemStyle"
android:layout_width="100dp"
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_marginRight="8dp"
android:ellipsize="marquee"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
app:layout_constraintHorizontal_bias="0.809"
app:layout_constraintLeft_toRightOf="@+id/store"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
In addition to getView method implementation, we need to define a Filter class in our custom adapter. AutoCompleteTextView calls performFiltering method passing user entered search string to it. In this method, we use Room DAO to get list of matching records from database for the search string.
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import zoftino.com.uicontrols.database.LocalDatabaseRepo;
import zoftino.com.uicontrols.database.StoreOffers;
public class StoreListAdapter extends ArrayAdapter {
private List<StoreOffers> dataList;
private Context mContext;
private int itemLayout;
private LocalDatabaseRepo localDatabaseRepo = new LocalDatabaseRepo();
private StoreListAdapter.ListFilter listFilter = new StoreListAdapter.ListFilter();
public StoreListAdapter(Context context, int resource, List<StoreOffers> storeDataLst) {
super(context, resource, storeDataLst);
dataList = storeDataLst;
mContext = context;
itemLayout = resource;
}
@Override
public int getCount() {
return dataList.size();
}
@Override
public StoreOffers getItem(int position) {
return dataList.get(position);
}
@Override
public View getView(int position, View view, @NonNull ViewGroup parent) {
if (view == null) {
view = LayoutInflater.from(parent.getContext())
.inflate(itemLayout, parent, false);
}
TextView strName = (TextView) view.findViewById(R.id.store);
strName.setText(getItem(position).getStoreName());
TextView couponCount = (TextView) view.findViewById(R.id.coupon);
couponCount.setText(""+getItem(position).getCouponsCount());
return view;
}
@NonNull
@Override
public Filter getFilter() {
return listFilter;
}
public class ListFilter extends Filter {
private Object lock = new Object();
@Override
protected FilterResults performFiltering(CharSequence prefix) {
FilterResults results = new FilterResults();
if (prefix == null || prefix.length() == 0) {
synchronized (lock) {
results.values = new ArrayList<String>();
results.count = 0;
}
} else {
final String searchStrLowerCase = prefix.toString().toLowerCase();
//Call to database to get matching records using room
List<StoreOffers> matchValues =
localDatabaseRepo.getStoreInfo(mContext, searchStrLowerCase);
results.values = matchValues;
results.count = matchValues.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.values != null) {
dataList = (ArrayList<StoreOffers>)results.values;
} else {
dataList = null;
}
if (results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
}
}
package zoftino.com.uicontrols;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AutoCompleteTextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import zoftino.com.uicontrols.database.StoreOffers;
public class DynamicSearchDatabaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.autocomplete);
AutoCompleteTextView nameTV = (AutoCompleteTextView)
findViewById(R.id.store);
List<StoreOffers> storeOffers = new ArrayList<StoreOffers>();
StoreListAdapter adapter = new StoreListAdapter(this,
R.layout.store_item, storeOffers);
nameTV.setAdapter(adapter);
nameTV.setOnItemClickListener(onItemClickListener);
}
private AdapterView.OnItemClickListener onItemClickListener =
new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(DynamicSearchDatabaseActivity.this,
"Clicked item from auto completion list "
+ adapterView.getItemAtPosition(i)
, Toast.LENGTH_SHORT).show();
}
};
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android.support.design="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TextInputLayout
android:id="@+id/store_til"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<AutoCompleteTextView
android:id="@+id/store"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter Store">
</AutoCompleteTextView>
</android.support.design.widget.TextInputLayout>
</android.support.constraint.ConstraintLayout>
package zoftino.com.uicontrols.database;
import android.arch.persistence.room.Database;
import android.arch.persistence.room.RoomDatabase;
@Database(entities = {StoreOffers.class}, version = 1)
public abstract class StoreInfoDatabase extends RoomDatabase {
public abstract StoreInfoDAO storeInfoDAO();
}
package zoftino.com.uicontrols.database;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;
@Entity
public class StoreOffers {
@PrimaryKey(autoGenerate = true)
private int id;
String storeName;
int couponsCount;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public int getCouponsCount() {
return couponsCount;
}
public void setCouponsCount(int couponsCount) {
this.couponsCount = couponsCount;
}
}
import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Query;
import java.util.List;
@Dao
public interface StoreInfoDAO {
@Query("SELECT * FROM StoreOffers WHERE storeName like :storeIn ")
List<StoreOffers> getStores(String storeIn);
}
package zoftino.com.uicontrols.database;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.room.OnConflictStrategy;
import android.arch.persistence.room.Room;
import android.arch.persistence.room.RoomDatabase;
import android.content.ContentValues;
import android.content.Context;
import java.util.List;
public class LocalDatabaseRepo {
private static StoreInfoDatabase storeInfoDatabase;
private StoreInfoDAO storeInfoDAO;
private static final Object LOCK = new Object();
private static RoomDatabase.Callback dbCallback = new RoomDatabase.Callback(){
public void onCreate (SupportSQLiteDatabase db){
}
public void onOpen (SupportSQLiteDatabase db){
//delete existing data
db.execSQL("Delete From StoreOffers");
ContentValues contentValues = new ContentValues();
contentValues.put("storeName", "Amazon");
contentValues.put("couponsCount", 30);
db.insert("StoreOffers", OnConflictStrategy.IGNORE, contentValues);
contentValues = new ContentValues();
contentValues.put("storeName", "AmPM");
contentValues.put("couponsCount", 13);
db.insert("StoreOffers", OnConflictStrategy.IGNORE, contentValues);
contentValues = new ContentValues();
contentValues.put("storeName", "BestFashion");
contentValues.put("couponsCount", 11);
db.insert("StoreOffers", OnConflictStrategy.IGNORE, contentValues);
contentValues = new ContentValues();
contentValues.put("storeName", "FashionStore");
contentValues.put("couponsCount", 53);
db.insert("StoreOffers", OnConflictStrategy.IGNORE, contentValues);
contentValues = new ContentValues();
contentValues.put("storeName", "FashionMall");
contentValues.put("couponsCount", 43);
db.insert("StoreOffers", OnConflictStrategy.IGNORE, contentValues);
contentValues = new ContentValues();
contentValues.put("storeName", "FashionSale");
contentValues.put("couponsCount", 23);
db.insert("StoreOffers", OnConflictStrategy.IGNORE, contentValues);
}
};
public synchronized static StoreInfoDatabase getStoreInfoDatabase(Context context){
if(storeInfoDatabase == null) {
synchronized (LOCK) {
if (storeInfoDatabase == null) {
storeInfoDatabase = Room.databaseBuilder(context,
StoreInfoDatabase.class, "store info db")
.addCallback(dbCallback).build();
}
}
}
return storeInfoDatabase;
}
public List<StoreOffers> getStoreInfo(Context context, String storeStr) {
if (storeInfoDAO == null) {
storeInfoDAO = LocalDatabaseRepo.getStoreInfoDatabase(context).storeInfoDAO();
}
return storeInfoDAO.getStores(storeStr+"%");
}
}