79

I am currently using an ActionBar menu item to display a SearchView in the action bar. When the search menu item is expanded the soft keyboard is displayed which is what I want. Now, when the user presses the back button to close the soft keyboard, I would also like to collapse the SearchView in the action bar.

I have tried implementing the following listeners OnKeyListener and OnFocusChangeListener on the MenuItem and the ActionView. I have also tried using OnBackPressed() in the Activity. None of the above detect when the back button is used to close the soft keyboard.

Any ideas?

I have implemented OnActionExpandListener to know when the SearchView is visible.

user1258568
  • 1,161
  • 1
  • 8
  • 7

11 Answers11

100

I'll expand on @user1258568 's answer for the lazy. This worked for me. Note that it clears your query when focus is lost.

final MenuItem searchMenuItem = optionsMenu.findItem(R.id.search);
final SearchView searchView = (SearchView) searchMenuItem.getActionView();

searchView.setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View view, boolean queryTextFocused) {
        if(!queryTextFocused) {
            searchMenuItem.collapseActionView();
            searchView.setQuery("", false);
        }
    }
});
Jared Rummler
  • 35,743
  • 18
  • 127
  • 142
Jon Willis
  • 6,839
  • 4
  • 38
  • 51
  • 1
    Works perfectly. If you add a searchView in code, you may need to use `menu.getItem(yourSearchViewItemPosition)` instead. – GeeF Feb 05 '13 at 14:05
  • @AlexBonel How so? I know that the S3 and S4 have idiot hardware buttons, but I'm unclear how that would affect this snippet. Perhaps it is because the ActionBar menu is hidden and searchMenuItem acts differently. I've got a S4 on hand, I'll have to test this. – Jon Willis Sep 04 '13 at 17:16
  • @JonWillis If it's not difficult for you, could you share your experience of such behaviour on S4 please? – Alex Bonel Sep 05 '13 at 07:01
  • 4
    Nice, after hours of research and implementations to try to do this I found your answer :) ... First thing I thought was to use an focus listener but I used `setOnFocusChangeListener()` instead of `setOnQueryTextFocusChangeListener()`. With your method everything works welll. Thank you. – Ionut Negru Jan 15 '14 at 21:22
  • 2
    @IonutNegru you're welcome! gotta love the Android SDK sometimes. I wanna see them rewrite it in a sane, modern way and drop legacy support, but that will never happen. – Jon Willis Mar 06 '14 at 01:29
  • Working fine, nice example :) – uniruddh Mar 14 '14 at 05:48
  • Not working in my Nexus7 because the search view does not lose focus when search submitted. See my answer. – GaRRaPeTa Oct 06 '14 at 12:39
  • @JonWillis did it work on S3? Just curious. And thanks for the answer. I came back to this answer after a year and it's still helpful! :) – Sufian Aug 27 '15 at 06:25
37

I found a better solution.

searchView.setOnQueryTextFocusChangeListener(). 

The OnQueryTextFocusChangeListener gets called when the keyboard is displayed or hidden. Gets called first when the keyboard is displayed and the search view will have focus. Gets called again when keyboard is hidden and search view will lose focus, can close search viewthen using

menuItem.collapseActionView().
NagarjunaReddy
  • 8,500
  • 10
  • 60
  • 94
user1258568
  • 1,161
  • 1
  • 8
  • 7
  • 5
    This is misleading. The `OnQueryTextFocusChangeListener` does NOT get called when the keyboard is displayed or hidden. It gets called only when the `searchView` comes in focus and or goes out of focus. So, when the `SearchView` is in focus for the first time, the `OnQueryTextFocusChangeListener` is called and the keyboard also is displayed. Now if the back button is pressed, the keyboard gets hidden but the `OnQueryTextFocusChangeListener` does not get called. On pressing the back button a second time, the `searchView` collapses and `OnQueryTextFocusChangeListener` is called. – faizal Dec 18 '13 at 11:52
11

Just Override onBackPressed like this:

@Override
    public void onBackPressed() {
        if (searchView.isShown()){
            searchView.onActionViewCollapsed();  //collapse your ActionView
            searchView.setQuery("",false);       //clears your query without submit
            isClosed = true;                     //needed to handle closed by back
        } else{
            super.onBackPressed();
        }
    }

and your onCreateOptionsMenu would inflate the mSearchView like this:

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.menu_search, menu);
        mSearchView = (SearchView) menu.findItem(R.id.menu_action_search).getActionView();
        mSearchView.setOnQueryTextListener(this);
        mSearchView.setOnSearchClickListener(this);
        mSearchView.setOnCloseListener(this);
        isClosed = true;
        return true;
    }

have you class implement the following like this:

public class myActivity extends FragmentActivity implements
    SearchView.OnQueryTextListener, View.OnClickListener, SearchView.OnCloseListener {

which you will also need:

@Override
public void onClick(View view) {
    isClosed = false;
}

@Override
public boolean onClose() {
    isClosed = true;
    return false;
}

You will need to make "mSearchView" and "isClosed" both global variables to the activity.

Codeversed
  • 8,437
  • 3
  • 38
  • 40
  • 3
    I don't think that works; in my experience the onBackPressed method is not called if the keyboard is up; it simply dismisses the keyboard and you need to press back again to get it to call that method. This is on 2.3; i haven't experimented with 3.x and 4.0 but i suspect this is the same there as well. – Kevlar Apr 26 '12 at 22:33
  • @Kevlar I think you may be correct. I will test this again and make sure there isn't a way. Seems my solution does slightly different of what they want. As far as I know I don't think there is a way to handle any back while the soft keyboard is shown. My solution handles after the keyboard is closed for sure. Will test on different SDK's. – Codeversed May 01 '12 at 07:32
  • 1
    i had tried to collapse the search using: searchMenuItem.collapseActionView(); but it turned out that searchView.onActionViewCollapsed(); did the trick for me -- thanks! – bkurzius Feb 28 '13 at 21:38
  • I don't think you should call directly on... functions. Those functions are not made to be called directly from your code... – user457015 Aug 06 '13 at 01:58
  • how to use collapseActionView() on api level 11. – madan V Nov 14 '13 at 07:08
  • @madanV If you use `android.support.v7.appcompat` this can be done using `MenuItemCompat.collapseActionView(menuItem)` which receives the menu search item in your actionbar. – acrespo Jul 01 '14 at 20:56
3

The answer from Jon Willis works great. This is an improvement to his answer.

First, create a new class that implements View.OnFocusChangeListener:

public class SearchViewFocusListener implements View.OnFocusChangeListener {

    private final MenuItem mMenuItem;

    public SearchViewFocusListener(MenuItem menuItem) {
        mMenuItem = menuItem;
    }

    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
            mMenuItem.collapseActionView();
            if (v instanceof SearchView) {
                ((SearchView) v).setQuery("", false);
            }
        }
    }

}

Next, set the listener on your SearchView:

searchView.setOnQueryTextFocusChangeListener(new SearchViewFocusListener(menuItem));
Jared Rummler
  • 35,743
  • 18
  • 127
  • 142
  • works nicely, but how do I clear the focus from the search icon? The FocusChangeListener captures a press of the Back Button on a device and that closes the soft keyboard and closes the SearchView and then the search icon appears as I would like. However, the search icon is highlighted (has focus) and I'd prefer it to not have focus. I tried a few ways to clear but with no luck. Any ideas? – AJW Oct 18 '19 at 00:30
2

You only need to put the "collapseActionView" attribute in the menu layout

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/menu_item_search"
        android:title="@string/search"
        android:iconifiedByDefault="true"
        android:icon="@drawable/ic_action_search" 
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="ifRoom|collapseActionView"/> <--this one
</menu>

That will give you the functionality you look for all by itself.Don't forget to call the method "clearFocus" on the SearchView to close the keyboard once you send the query.

1

This is what I did for making the keyboard disappear. You can try to see if this works for you. I set the searchView to invisible and then to visible again.

    //set query change listener
     searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener(){
        @Override
        public boolean onQueryTextChange(String newText) {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public boolean onQueryTextSubmit(String query) {
            /**
             * hides and then unhides search tab to make sure keyboard disappears when query is submitted
             */
                  searchView.setVisibility(View.INVISIBLE);
                  searchView.setVisibility(View.VISIBLE);
            return false;
        }

     });
Parnit
  • 1,024
  • 1
  • 8
  • 16
  • Worked for me, too, but you have to add getActivity().supportInvalidateOptionsMenu(); to restore action bar – almisoft Dec 11 '13 at 11:37
1

It's achievable like this:

   private void setupSearchView(Menu menu) {
        final MenuItem searchMenuItem = menu.findItem(R.id.action_search);
        final SearchView searchView = (SearchView) searchMenuItem.getActionView();

        [...]

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                searchMenuItem.collapseActionView();
                return false;
            }
            @Override
            public boolean onQueryTextChange(String newText) {
                return true;
            }
        });
    }

Solutions based on setOnQueryTextFocusChangeListener() did not work for me because the event was not launched - the searchView did not lose focus when submitted, probably because I perform the search in the same activity that contains the Search View.

Anyway, I think using OnQueryTextListener is more correct, as it describes the event of submitting text more precisely.

GaRRaPeTa
  • 4,859
  • 4
  • 30
  • 52
0
@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getSupportMenuInflater().inflate(R.menu.home_screen, menu);

        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        final MenuItem searchMenuItem = menu.findItem(R.id.menu_search);
        final SearchView searchView = (SearchView) searchMenuItem
                .getActionView();
        searchView.setIconifiedByDefault(false);
        if (searchManager != null && searchView != null) {
            searchView.setSearchableInfo(searchManager
                    .getSearchableInfo(getComponentName()));

            searchView
                    .setOnQueryTextFocusChangeListener(new View.OnFocusChangeListener() {

                        @Override
                        public void onFocusChange(View v, boolean hasFocus) {

                            if (!hasFocus) {
                                if (searchMenuItem != null) {
                                    searchMenuItem.collapseActionView();
                                }// end if
                                if (searchView != null) {
                                    searchView.setQuery("", false);

                                }// end if
                            }// end if

                        }
                    });

            searchView
                    .setOnQueryTextListener(new SearchView.OnQueryTextListener() {

                        @Override
                        public boolean onQueryTextSubmit(String query) {
                            /**
                             * hides and then unhides search tab to make sure
                             * keyboard disappears when query is submitted
                             */
                            if (searchView != null) {
                                searchView.setVisibility(View.INVISIBLE);
                                searchView.setVisibility(View.VISIBLE);

                            }
                            return false;
                        }

                        @Override
                        public boolean onQueryTextChange(String newText) {
                            // TODO Auto-generated method stub
                            return false;
                        }
                    });

        }

        return super.onCreateOptionsMenu(menu);
    }
confucius
  • 12,529
  • 10
  • 45
  • 66
0

If you want to collapse keyboard when user clicks search icon on keyboard this can be achieved by simple

inside onquerytextsubmitted {

searchView.clearfocus()

}

geniushkg
  • 684
  • 8
  • 19
0

You need to call setIconified twice.

To actually collapse your search view and close the keyboard.
With first call text of search view is cleared with second call keyboard and search view get closed.

David 'mArm' Ansermot
  • 5,742
  • 6
  • 40
  • 77
nosaiba darwish
  • 1,011
  • 10
  • 13
0

For some reason, menuItem.collapseActionView() did not work so I used searchView.setIconified(true) instead.

This gives the below result as the code sample.

final MenuItem searchItem = (MenuItem) menu.findItem(R.id.menu_item_search);
final SearchView searchView = (SearchView) searchItem.getActionView();

searchView.setOnQueryTextFocusChangeListener(new SearchView.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
            searchView.setIconified(true);
        }
    }
});
bastien
  • 2,447
  • 1
  • 10
  • 20