I have a collapsible SearchView
in my ActionBar
. After a search has been performed, the associated ListView
is filtered to show only matching items. During that state, the SearchView
is still expanded and shows the search string. If the user closes the SearchView
, I remove the filter.
I want to restore the search state, e.g. on configuration change or if the activity was destroyed when I return to it from another activity.
I restore the query string in onRestoreInstanceState()
, and if I find a query string in onCreateOptionsMenu()
I call
searchView.setQuery(query, true);
so as to execute the query again. It turned out that this is better than applying the query filter immediately onRestoreInstanceState()
. With the latter the list is shortly shown unfiltered, only then the query is applied again. With setQuery()
this does not happen.
Problem: The query is executed and the list is filtered, but the search view remains collapsed. Therefore the user cannot use the search view to remove the filter or to apply another query.
In onCreateOptionsMenu()
I can be sure that the search item and the search view exists, therefore I can call searchItem.expandActionView()
. Strangely, only this really expands the ActionView
- calling setIconified(false)
does not expand the view, not even when it is called twice in a row.
If I use expandActionView()
before I call setQuery()
, the SearchView
is opened and shows the text (otherwise expandActionView()
empties the SearchView
).
Unfortunately, expandActionView()
has a side effect: the suggestion list is also shown and the keyboard opens.
I can hide the keyboard using searchView.clearFocus()
. So the remaining problem is the suggestion list. How can I close an open suggestion list on a SearchView
that has text?
I wonder if there is a better way to restore a search view in the action bar that does not have so many side effects.
I have found a workaround. It is extremely ugly, but it works.
So here is how I was able to restore the search box instance state after a configuration change.
First of all, restore the query string in onRestoreInstanceState
currentQuery = state.getString(KEY_SAVED_FILTER_CONSTRAINT);
In onCreateOptionsMenu set up the search view and if there is a currentQuery, expand the search item and submit the query again. Clear the focus to hide the keyboard.
MenuItem searchItem = menu.findItem(R.id.action_search);
searchView = (SearchView) searchItem.getActionView();
searchView.setSearchableInfo(searchManager
.getSearchableInfo(getComponentName()));
if (!TextUtils.isEmpty(currentQuery)) {
searchItem.expandActionView();
searchView.setQuery(currentQuery, true);
searchView.clearFocus();
}
Finally we need to close the suggestion list. Here is how to get the query text view from the search view:
private AutoCompleteTextView findQueryTextView(ViewGroup viewGroup) {
AutoCompleteTextView queryTextView = null;
if (viewGroup != null) {
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = viewGroup.getChildAt(i);
if (child instanceof AutoCompleteTextView) {
queryTextView = (AutoCompleteTextView) child;
break;
} else if (child instanceof ViewGroup) {
queryTextView = findQueryTextView((ViewGroup) child);
if (queryTextView != null) {
break;
}
}
}
}
return queryTextView;
}
Then you can dismiss the suggestion list:
AutoCompleteTextView queryTextView = findQueryTextView(searchView);
if (queryTextView != null) {
queryTextView.dismissDropDown();
}
This does however not work from within onCreateOptionsMenu. I had to move the invocation of dismissDropDown into the onNewIntent method of my activity to make it work.
What makes this so ugly is the fact that the restoration steps are dispersed over various phases of the lifecycle and that a recursive view search is necessary to get to the suggestion list.