Android: Providing recent search suggestions without searchable activity?
android, java, searchview
Solution
Ok, I spent my time for this. I make my own simple suggestion implementation from `SQLiteDatabase`.
We will create 3 classes like the following
- `MainActivity` - for test of `SearchView` suggestion from database
- `SuggestionDatabase` - this will store your recent search keyword.
- `SuggestionSimpleCursorAdapter` - this is a subclass of `SimpleCursorAdapter`. I'll explain why I make this class instead of using `SimpleCursorAdapter`.
The codes
// MainActivity.java
public class MainActivity
extends Activity
implements SearchView.OnQueryTextListener,
SearchView.OnSuggestionListener
{
private SuggestionsDatabase database;
private SearchView searchView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
database = new SuggestionsDatabase(this);
searchView = (SearchView) findViewById(R.id.searchView1);
searchView.setOnQueryTextListener(this);
searchView.setOnSuggestionListener(this);
}
@Override
public boolean onSuggestionSelect(int position) {
return false;
}
@Override
public boolean onSuggestionClick(int position) {
SQLiteCursor cursor = (SQLiteCursor) searchView.getSuggestionsAdapter().getItem(position);
int indexColumnSuggestion = cursor.getColumnIndex( SuggestionsDatabase.FIELD_SUGGESTION);
searchView.setQuery(cursor.getString(indexColumnSuggestion), false);
return true;
}
@Override
public boolean onQueryTextSubmit(String query) {
long result = database.insertSuggestion(query);
return result != -1;
}
@Override
public boolean onQueryTextChange(String newText) {
Cursor cursor = database.getSuggestions(newText);
if(cursor.getCount() != 0)
{
String[] columns = new String[] {SuggestionsDatabase.FIELD_SUGGESTION };
int[] columnTextId = new int[] { android.R.id.text1};
SuggestionSimpleCursorAdapter simple = new SuggestionSimpleCursorAdapter(getBaseContext(),
android.R.layout.simple_list_item_1, cursor,
columns , columnTextId
, 0);
searchView.setSuggestionsAdapter(simple);
return true;
}
else
{
return false;
}
}
}
How it works
- When user taps the search button, the `onQueryTextSubmit()` will be triggered and then the search keyword will be saved in our database. Let's say we submit a keyword "Hello"
- If the user writes a string for example "Hel" or "H" in `SearchView` the `onQueryTextChange()` will be called and then we search this keyword in `SQLiteDatabase` (`SuggestionDatabase`). If "Hel" or "H" matches "Hello" , display the results of query by setting the returned Cursor in `SuggestionSimpleCursorAdapter` and then set this adapter in `SearchView`. Here's the picture.
3. Ofcourse we will tap the suggestion which is "Hello", `onSuggestionClick(int position)` will be called for that. We get the `SQLiteCursor` object from the `SearchView`'s adapter (`SuggestionSimpleCursorAdapter`) and get the Suggestion text from it, set the suggestion text in `SearchView` object
SQLiteCursor cursor = (SQLiteCursor) searchView.getSuggestionsAdapter().getItem(position);
int indexColumnSuggestion = cursor.getColumnIndex( SuggestionsDatabase.FIELD_SUGGESTION);
searchView.setQuery(cursor.getString(indexColumnSuggestion), false);
If we use `SimpleCursorAdapter` it also works properly but let's say we have this scenario
- If We run this program in smartphone and type the keyword "Hel", the suggestion will appear properly.
- What if We rotate the screen in landscape? It will switch in Full Screen mode and you can still type the keyword.
What will happen in suggestion? Let's take a look.
See the weird suggestion? How we solve that? By overriding the `convertToString(Cursor cursor)` which returns a `CharSequence`
// SuggestionSimpleCursorAdapter.java
public class SuggestionSimpleCursorAdapter
extends SimpleCursorAdapter
{
public SuggestionSimpleCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
}
public SuggestionSimpleCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
@Override
public CharSequence convertToString(Cursor cursor) {
int indexColumnSuggestion = cursor.getColumnIndex(SuggestionsDatabase.FIELD_SUGGESTION);
return cursor.getString(indexColumnSuggestion);
}
}
By overriding `convertToString(Cursor cursor)` , here's the result
And here's the database
// SuggestionDatabase.java
public class SuggestionsDatabase {
public static final String DB_SUGGESTION = "SUGGESTION_DB";
public final static String TABLE_SUGGESTION = "SUGGESTION_TB";
public final static String FIELD_ID = "_id";
public final static String FIELD_SUGGESTION = "suggestion";
private SQLiteDatabase db;
private Helper helper;
public SuggestionsDatabase(Context context) {
helper = new Helper(context, DB_SUGGESTION, null, 1);
db = helper.getWritableDatabase();
}
public long insertSuggestion(String text)
{
ContentValues values = new ContentValues();
values.put(FIELD_SUGGESTION, text);
return db.insert(TABLE_SUGGESTION, null, values);
}
public Cursor getSuggestions(String text)
{
return db.query(TABLE_SUGGESTION, new String[] {FIELD_ID, FIELD_SUGGESTION},
FIELD_SUGGESTION+" LIKE '"+ text +"%'", null, null, null, null);
}
private class Helper extends SQLiteOpenHelper
{
public Helper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "+TABLE_SUGGESTION+" ("+
FIELD_ID+" integer primary key autoincrement, "+FIELD_SUGGESTION+" text);");
Log.d("SUGGESTION", "DB CREATED");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
I hope this answer is useful to other programmers alot. :)
Problem
I have an ActionBar SearchView and I am successfully able to make searches with it. The android documentation does not explain how to implement search suggestions. I do not want to have a searchable activity. This is my search code: ``` public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_add_song, menu); final SearchView searchView = (SearchView) menu.findItem(R.id.song_search).getActionView(); searchView.setFocusable(true); searchView.setIconified(false); final AddSongActivity activity = this; searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextChange(String newText) { // Do nothing return true; } @Override public boolean onQueryTextSubmit(String query) { // Clear SearchView searchView.clearFocus(); // Begin Spotify Search TextView notice = (TextView)findViewById(R.id.search_notice); URL url; try { url = new URL("http://ws.spotify.com/search/1/track.json?q=" + URLEncoder.encode(query,"UTF-8")); } catch (MalformedURLException e) { notice.setText("Malformed Search"); notice.setHeight(noticeHeight); return true; } catch (UnsupportedEncodingException e) { notice.setText("Unsupported Encoding. Maybe a problem with your device."); notice.setHeight(noticeHeight); return true; } new SearchDownload(url, activity).execute(); notice.setText("Loading Tracks"); notice.setHeight(noticeHeight); Log.i("infodb","" + noticeHeight); return true; } }); ``` This works for searching but I have no idea to implement recent search query suggestions. How do I go about doing this? Thank you.