Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a better way to restore SearchView state?

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.

like image 692
dschulten Avatar asked Mar 19 '14 06:03

dschulten


2 Answers

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.

like image 62
dschulten Avatar answered Oct 16 '22 14:10

dschulten


In my humble opinion. When you want to restore searchView state, we should use onSaveInstanceState(Bundle outState). Called to retrieve per-instance state from an activity before being killed so that the state can be restored in onCreate(Bundle) or onRestoreInstanceState(Bundle) (the Bundle populated by this method will be passed to both).

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // if you saved something on outState you can recover them here
    if (savedInstanceState != null) {
             mSearchString = savedInstanceState.getString(SEARCH_KEY);
    }
}

// This is called before the activity is destroyed
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mSearchString = mSearchView.getQuery().toString();
    outState.putString(SEARCH_KEY, mSearchString);
}

This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. Now when user changes the orientation, the state of the user interface can be restored via onCreate(Bundle) or onRestoreInstanceState(Bundle).

Now we should use onCreateOptionsMenu(Menu menu), because we need to focus our SearchView.

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);

    MenuItem searchMenuItem = menu.findItem(R.id.menu_main_action_search);

    mSearchView = (SearchView) MenuItemCompat.getActionView(searchMenuItem);

    //focus the SearchView
    if (mSearchString != null && !mSearchString.isEmpty()) {
        searchMenuItem.expandActionView();
        mSearchView.setQuery(mSearchString, true);
        mSearchView.clearFocus();
    }

    return super.onCreateOptionsMenu(menu);
}

Also you can check this link with complete code.

like image 29
Cabezas Avatar answered Oct 16 '22 13:10

Cabezas