Book Excerpt: Full-Text Indexing and Searching (Part 4)
The following sections are excerpts from Version 6.9 of “The Busy Coder’s Guide to Android Development”, with slight modifications to fit the blog format. It continues the blog post series begun Monday.
As is covered in the chapter on advanced action bar techniques,
a SearchView
can be used to provide the standard “magnifying glass” search
icon in the action bar. When tapped, the action bar item expands into a field
where the user can type something, which our code can then receive and use to
update the UI. In the SearchView
sample from the action bar chapter, we saw
using a SearchView
for filtering. This time, we will use a SearchView
for searching.
For a search, we need to know when the user is done typing, which is usually
done by the user clicking a submit button. Hence, our code to configure
the SearchView
(a configureSearchView()
method in QuestionsFragment
)
calls setSubmitButtonEnabled(true)
:
private void configureSearchView(Menu menu) {
MenuItem search=menu.findItem(R.id.search);
search.setOnActionExpandListener(this);
sv=(SearchView)search.getActionView();
sv.setOnQueryTextListener(this);
sv.setSubmitButtonEnabled(true);
sv.setIconifiedByDefault(true);
if (initialQuery != null) {
sv.setIconified(false);
search.expandActionView();
sv.setQuery(initialQuery, true);
}
}
This, in turn, means that we need to pay attention to onQueryTextSubmit()
in our SearchView.OnQueryTextListener
implementation. That interface is
implemented on QuestionsFragment
itself, and delegates its work to a
doSearch()
method:
@Override
public boolean onQueryTextSubmit(String query) {
doSearch(query);
return(true);
}
That method, in turn, confirms that the search is different than the last one we
did (so we do not waste time running the search again), disables the SearchView
,
and posts a SearchRequestedEvent
on the EventBus
with the user’s search string:
private void doSearch(String match) {
if (!match.equals(lastQuery)) {
lastQuery=match;
if (sv != null) {
sv.setEnabled(false);
}
EventBus.getDefault().post(new SearchRequestedEvent(match));
}
}
That event is picked up by onEventBackgroundThread()
on ModelFragment
.
The method name onEventBackgroundThread()
means that the event will be
delivered to us on an EventBus-supplied background thread, so we can perform
database I/O. In it, we call loadQuestions()
on the DatabaseHelper
to
perform the search, and post another sticky ModelLoadedEvent
to update
the UI with the search results and re-enable the SearchView
:
public void onEventBackgroundThread(SearchRequestedEvent event) {
try {
Cursor results=DatabaseHelper.getInstance(app).loadQuestions(app, event.match);
EventBus.getDefault().postSticky(new ModelLoadedEvent(results));
}
catch (Exception e) {
Log.e(getClass().getSimpleName(),
"Exception searching database", e);
}
}
When the user clears the SearchView
, such as by pressing the BACK button a
few times, the onMenuItemActionCollapse()
method of QuestionsFragment
calls a clearSearch()
method:
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
clearSearch();
return(true);
}
That clearSearch()
method simply posts another SearchRequestedEvent
, this time
to load a fresh roster of all questions:
private void clearSearch() {
if (lastQuery!=null) {
lastQuery=null;
sv.setEnabled(false);
EventBus.getDefault().post(new SearchRequestedEvent(null));
}
}
In tomorrow’s post, we will look at how you can use SQLite’s FTS tables to get “snippets” back, to help provide some context around your search results.