Contents
Search function implementation
The usage of SearchFragment of Leanback support library and UI related implementation are explained in previous chapter. In this chapter, I will explain (background) search function logic.
Most of the implementation in this chapter is just a explanation of googlesamples.
In-app search algorithm
Search algorithm is implemented in loadRows method, especially the main logic is in doInBackground method of AsyncTask. It is actually simple, just checking the string “query” is contained in either title or description of Movie items.
The UI related task Adapter handling is done in UI thread. onPreExecute initializes mRowsAdapter, which will contain the search results, by clear method. Searching itself is executed in background thread in doInBackground, and it makes new ListRow which contains the search results. This listRow is added to Adapter in UI thread by onPostExecute method.
private void loadRows() { // offload processing from the UI thread new AsyncTask<String, Void, ListRow>() { private final String query = mQuery; @Override protected void onPreExecute() { mRowsAdapter.clear(); } @Override protected ListRow doInBackground(String... params) { final List<Movie> result = new ArrayList<>(); for (Movie movie : mItems) { // Main logic of search is here. // Just check that "query" is contained in Title or Description or not. if (movie.getTitle().toLowerCase(Locale.ENGLISH) .contains(query.toLowerCase(Locale.ENGLISH)) || movie.getDescription().toLowerCase(Locale.ENGLISH) .contains(query.toLowerCase(Locale.ENGLISH))) { result.add(movie); } } ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter()); listRowAdapter.addAll(0, result); HeaderItem header = new HeaderItem("Search Results"); return new ListRow(header, listRowAdapter); } @Override protected void onPostExecute(ListRow listRow) { mRowsAdapter.add(listRow); } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }
Execute search
Since search algorithm is already implemented above, we only need to call this function to get search results.
@Override public ObjectAdapter getResultsAdapter() { Log.d(TAG, "getResultsAdapter"); // Delete previously implemented mock code. // mRowsAdapter (Search result) is already prepared in loadRows method return mRowsAdapter; } ... @Override public boolean onQueryTextSubmit(String query) { Log.i(TAG, String.format("Search Query Text Submit %s", query)); mQuery = query; loadRows(); return true; }
Call loadRows method in onQueryTextSubmit method. Easy implementation is already done! let’s try build and run the code here.
We have already finished minimum implementation for search function. Next step is to enable search even during the user’s text typing.
Dynamic search execution
To detect when user typing search query, we can use onQueryTextChange method. So basic concept is to just execute loadRows method in onQueryTextChange to implement dynamic (during user input) search. However, onQueryTextChange will be executed every time user input one words, and we should not call loadRows method when it has already executed. Here, Handler is used to manage executing loadRows method.
private static final long SEARCH_DELAY_MS = 1000L; private final Handler mHandler = new Handler(); private final Runnable mDelayedLoad = new Runnable() { @Override public void run() { loadRows(); } }; ... /** * Starts {@link #loadRows()} method after delay. * @param query the word to be searched * @param delay the time to wait until loadRows will be executed (milliseconds). */ private void loadQueryWithDelay(String query, long delay) { mHandler.removeCallbacks(mDelayedLoad); if (!TextUtils.isEmpty(query) && !query.equals("nil")) { mQuery = query; mHandler.postDelayed(mDelayedLoad, delay); } }
loadQueryWithDelay method uses Handler to post loadRows task with a little delay so that loadRows task is not executed too frequently. The last modification is to call this loadQueryWithDelay method from onQueryTextChange.
@Override public boolean onQueryTextChange(String newQuery){ Log.i(TAG, String.format("Search Query Text Change %s", newQuery)); loadQueryWithDelay(newQuery, SEARCH_DELAY_MS); return true; } @Override public boolean onQueryTextSubmit(String query) { Log.i(TAG, String.format("Search Query Text Submit %s", query)); // No need to delay(wait) loadQuery, since the query typing has completed. loadQueryWithDelay(query, 0); return true; }
Build and run
As can be seen by video, search has executed even during the user is typing search query.
Source code is on github.