Android: Providing recent search suggestions without searchable activity?

Matthew Mitchell picture Matthew Mitchell · Dec 5, 2012 · Viewed 18.3k times · Source

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.

Answer

user948620 picture user948620 · Dec 8, 2012

Ok, I spent my time for this. I make my own simple suggestion implementation from SQLiteDatabase.

We will create 3 classes like the following

  1. MainActivity - for test of SearchView suggestion from database
  2. SuggestionDatabase - this will store your recent search keyword.
  3. 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

  1. 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"
  2. 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.

enter image description here
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

  1. If We run this program in smartphone and type the keyword "Hel", the suggestion will appear properly.

enter image description here

  1. 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.

enter image description here

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

enter image description here

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. :)