109

I'm trying to add support for the SearchView in the Android 3.0+ ActionBar, but I can't get the OnCloseListener to work.

Here's my code:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu, menu);
    searchView = (SearchView) menu.findItem(R.id.search_textbox).getActionView();
    searchView.setOnQueryTextListener(new OnQueryTextListener() {
        @Override
        public boolean onQueryTextChange(String newText) {
            searchLibrary(newText);
            return false;
        }
        @Override
        public boolean onQueryTextSubmit(String query) { return false; }
    });
    searchView.setOnCloseListener(new OnCloseListener() {
        @Override
        public boolean onClose() {
            System.out.println("Testing. 1, 2, 3...");
            return false;
        }
    });
    return true;
}

The search works great and every is working except for the OnCloseListener. Nothing is being printed to Logcat. Here's the Logcat for when I'm pressing the "Close" button:

02-17 13:01:52.914: I/TextType(446): TextType = 0x0
02-17 13:01:57.344: I/TextType(446): TextType = 0x0
02-17 13:02:02.944: I/TextType(446): TextType = 0x0

I've looked through the documentation and samples, but nothing seemed to change it. I'm running it on a Asus Transformer Prime and a Galaxy Nexus, both on Ice Cream Sandwich. Any ideas?

Update:

Yes - System.out.println() does work. Here's proof:

   @Override
 public boolean onQueryTextChange(String newText) {
    System.out.println(newText + "hello");
    searchLibrary(newText);
    return false;
 }

Results in this Logcat:

02-17 13:04:20.094: I/System.out(21152): hello
02-17 13:04:24.914: I/System.out(21152): thello
02-17 13:04:25.394: I/System.out(21152): tehello
02-17 13:04:25.784: I/System.out(21152): teshello
02-17 13:04:26.064: I/System.out(21152): testhello
Michell Bak
  • 13,182
  • 11
  • 64
  • 121
  • Hmm, Works OK for me with Android 3.2 but NOT for 4.0+ – PJL Feb 17 '12 at 17:59
  • 10
    Raised bug, [25758](http://code.google.com/p/android/issues/detail?id=25758) – PJL Feb 20 '12 at 10:27
  • 3
    I'm glad it is not just me having this problem. Anyone have any other hacks other than the one below? – bencallis Mar 07 '12 at 09:57
  • 2
    I have learnt two things if `showAsAction` is set to `always`. Search box has a **close** button of its own but if it is set to `ifRoom | collapseActionView` it expands on the action bar. – Beraki Sep 07 '16 at 15:05

19 Answers19

157

I also meet this problem, and I have no choice but give up "oncloselistener". Instead, you can get your menuItem, then setOnActionExpandListener. Then override unimplents methods.

@Override
public boolean onMenuItemActionExpand(MenuItem item) {
    // TODO Auto-generated method stub
    Log.d("*******","onMenuItemActionExpand");
    return true;
}

@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
    //do what you want to when close the sesarchview
    //remember to return true;
    Log.d("*******","onMenuItemActionCollapse");
    return true;
}
niki huang
  • 1,686
  • 1
  • 11
  • 5
  • I believe we're only talking about ActionBar's SearchView which is only honeycomb+ – NKijak Jan 11 '13 at 15:53
  • don't bother using onCloseListener, just use this one with your menu item. – Robert Dec 06 '13 at 08:14
  • 12
    I think you can use MenuItemCompat.OnActionExpandListener for earlier API levels: http://developer.android.com/reference/android/support/v4/view/MenuItemCompat.OnActionExpandListener.html – Ripityom Feb 04 '14 at 11:43
  • 2
    Full answer: `if (Build.VERSION.SdkInt > BuildVersionCodes.NMr1) item.SetOnActionExpandListener(this); else MenuItemCompat.SetOnActionExpandListener(item, this);` – FindOutIslamNow Aug 14 '17 at 07:23
62

For Android API 14+ (ICS and greater) use this code:

// When using the support library, the setOnActionExpandListener() method is
// static and accepts the MenuItem object as an argument
MenuItemCompat.setOnActionExpandListener(menuItem, new OnActionExpandListener() {
    @Override
    public boolean onMenuItemActionCollapse(MenuItem item) {
        // Do something when collapsed
        return true;  // Return true to collapse action view
    }

    @Override
    public boolean onMenuItemActionExpand(MenuItem item) {
        // Do something when expanded
        return true;  // Return true to expand action view
    }
});

For more information: http://developer.android.com/guide/topics/ui/actionbar.html#ActionView

Ref: onActionCollapse/onActionExpand

Avinash R
  • 3,101
  • 1
  • 27
  • 47
lomza
  • 9,412
  • 15
  • 70
  • 85
31

For this problem I came up with something like this,

private SearchView mSearchView;

@TargetApi(14)
@Override
public boolean onCreateOptionsMenu(Menu menu)
{

    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.conversation_index_activity_menu, menu);

    mSearchView = (SearchView) menu.findItem(R.id.itemSearch).getActionView();

    MenuItem menuItem = menu.findItem(R.id.itemSearch);

    int currentapiVersion = android.os.Build.VERSION.SDK_INT;
    if (currentapiVersion >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    {
        menuItem.setOnActionExpandListener(new OnActionExpandListener()
        {

            @Override
            public boolean onMenuItemActionCollapse(MenuItem item)
            {
                // Do something when collapsed
                Log.i(TAG, "onMenuItemActionCollapse " + item.getItemId());
                return true; // Return true to collapse action view
            }

            @Override
            public boolean onMenuItemActionExpand(MenuItem item)
            {
                // TODO Auto-generated method stub
                Log.i(TAG, "onMenuItemActionExpand " + item.getItemId());
                return true;
            }
        });
    } else
    {
        // do something for phones running an SDK before froyo
        mSearchView.setOnCloseListener(new OnCloseListener()
        {

            @Override
            public boolean onClose()
            {
                Log.i(TAG, "mSearchView on close ");
                // TODO Auto-generated method stub
                return false;
            }
        });
    }


    return super.onCreateOptionsMenu(menu);

}
Andro Selva
  • 53,910
  • 52
  • 193
  • 240
Ben Benson
  • 336
  • 3
  • 4
20

I ran into same problem on android 4.1.1. Looks like it is a known bug: https://code.google.com/p/android/issues/detail?id=25758

Anyway, as a workaround i used state change listener (when SearchView is detached from action bar, it is also closed obviously).

view.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {

    @Override
    public void onViewDetachedFromWindow(View arg0) {
        // search was detached/closed
    }

    @Override
    public void onViewAttachedToWindow(View arg0) {
        // search was opened
    }
});

Above code worked well in my case.


I post same answer here: https://stackoverflow.com/a/24573266/2162924

Community
  • 1
  • 1
Dario
  • 2,053
  • 21
  • 31
  • Yes, that bug was created right after I posted this question. See the comments on the original question. – Michell Bak Jul 04 '14 at 12:22
  • Oh ok, see now. But anyway, maybe this workaround with state change listener might be useful for others also. – Dario Jul 04 '14 at 13:17
  • While disappointing that OnCloseListener doesn't work as you'd think, this is actually a nice, clean solution. Kudos! – welshk91 Apr 19 '16 at 00:33
12

I ended up using a bit of a hack, that works well for my purpose - not sure it'll work with all purposes. Anyway, I'm doing a check to see if the search query is empty. This is not really related to the SearchView's OnCloseListener though - that still doesn't work!

searchView.setOnQueryTextListener(new OnQueryTextListener() {
            @Override
            public boolean onQueryTextChange(String newText) {
                if (newText.length() > 0) {
                    // Search
                } else {
                    // Do something when there's no input
                }
                return false;
            }
            @Override
            public boolean onQueryTextSubmit(String query) { return false; }
        });
Michell Bak
  • 13,182
  • 11
  • 64
  • 121
11

Well, this solved my problem:

Menu item with showAsAction="always"

<item
    android:id="@+id/action_search"
    android:icon="@drawable/ic_action_search"
    android:title="Search"
    app:actionViewClass="android.support.v7.widget.SearchView"
    app:showAsAction="always"/>

and in activity

searchView.setOnCloseListener(new OnCloseListener() {

        @Override
        public boolean onClose() {

            Log.i("SearchView:", "onClose");
            searchView.onActionViewCollapsed();
            return false;
        }
    });
Sushant
  • 1,272
  • 2
  • 15
  • 31
  • 1
    Setting `always` value to `showAsAction` attribute solves the problem. The important thing is that when `always` value `is not` present in `showAsAction`, expanded `SearchView` presents close button(cross icon) only if query in `SearchView` is non-null String. The `SearchView.onCloseClicked` that handles close button events tells that callback to `OnCloseListener` is called only if query is empty, if it's not - it's cleared first - but then, after clearing query close button disappears and we are unable to deliver `onClose` callback to `OnCloseListener` – bpawlowski Feb 24 '16 at 20:19
  • 1
    Just a hint: this method is called when the user closes the search view (quite obvious, but it took me a few times to realize). The fist time the user click on the X it erases text, and I didnt get that update, on the second click on the X it closes the SearchView and the onClose is invoked. Hope it helps! – Federico Alvarez Sep 19 '17 at 18:15
5

In order to make the OnCloseListener work, make sure that showAsAction is set to always in the search menu item.

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      tools:context=".SearchActivity">

    <item
        android:id="@+id/search"
        android:title="@string/search"
        android:icon="@drawable/ic_search_toolbar"
        app:showAsAction="always"
        app:actionViewClass="android.support.v7.widget.SearchView"/>
</menu>
Julio Betta
  • 2,275
  • 1
  • 25
  • 25
3

Create the menu item with the app:showAsAction set to always.

<item   
 android:id="@+id/action_search"  
 android:title="..."  
 android:icon="..."  
 app:actionViewClass="android.support.v7.widget.SearchView"  
 app:showAsAction="always"/>

When creating the SearchView in the onCreateOptionsMenu method do something similar

inflater.inflate(R.menu.menu_search, menu);
final MenuItem item = menu.findItem(R.id.action_search);
final SearchView search = (SearchView) item.getActionView();
search.setQueryHint(getString(R.string.search_brand_item));
search.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
  @Override
  public boolean onQueryTextSubmit(String query) {
    // add your code
    return false;
  }

  @Override
  public boolean onQueryTextChange(String newText) {
    // add your code 
    return false;
  }
});
search.setOnCloseListener(new SearchView.OnCloseListener() {
  @Override
  public boolean onClose() {
    // add your code here
    return false;
  }
});
search.setIconifiedByDefault(true); // make sure to set this to true

The search.setIconifiedByDefault(true) needs to be set to true to call the onClose() method on the SearchView.OnCloseListener() created above.

Ray Hunter
  • 15,137
  • 5
  • 53
  • 51
  • 1
    Just for anyone who wants to understand what setIconifiedByDefault() does, It Sets the default or resting state of the search field. If true, a single search icon is shown by default and expands to show the text field and other buttons when pressed. Also, if the default state is iconified, then it collapses to that state when the close button is pressed. – Anubhav Jun 06 '21 at 12:31
3
    searchView.setOnCloseListener {
        d("click", "close clicked")
        return@setOnCloseListener false
    }

if you click on close searchView ->

D/click: close clicked

  • 2
    Answers with a blob of code and some cryptic words isn't particularly clear. Just use natural language to describe why you think this solves the problem, and then show the implementation of the solution. –  Sep 20 '18 at 15:11
  • I did not understand this cryptic "explanation" either, but it still led me to the correct solution in my case. My search view wasn't closing and my `setOnCloseListener ` is very simple. I compared mine with the one from this answer and noticed, that I return `true`. Since I changed it to `false` it works fine, now. It's easy to miss such little things, so it helps to compare your own code to foreign examples. – Akito Dec 14 '21 at 03:37
2

I have encountered the same problem with onCloseListener not invoking for the SearchView. Understand from the bug issue raised in 25758, and some postings I have read, to invoke onCloseListener, you need to set:

searchView.setIconifiedByDefault(true);

But for my case I wanted to have the search view opened & not iconified all the time. I manage to resolve this by adding one more line below:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.search_bar, menu);
    SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    searchView = (SearchView) menu.findItem(R.id.search).getActionView();
    searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
    searchView.setOnQueryTextListener(queryTextListener);
    searchView.setIconifiedByDefault(true);
    searchView.setIconified(false);
    return true;
}

The searchView.setIconified(false) will cause the searchView to open up, despite setting the default to iconified to true in the previous line. In this way, I managed to have both a SearchView that opens up all the time & having it invoke the onCloseListener.

gkl
  • 81
  • 4
1

For MenuItemCompat problem I added ViewTreeObserver to track the visibility state. You can check my answer here: https://stackoverflow.com/a/28762632/1633609

Community
  • 1
  • 1
1

I used the SearchView close button and set a setOnClickListener on it

searchView.findViewById<ImageView>(R.id.search_close_btn).setOnClickListener {
    searchView.setQuery("", false)
    searchView.clearFocus()
}
Alexander
  • 75
  • 2
  • 7
0

The reason the OnCloseListener is not called is because there is a bug in the Android code -- the listener is only called if you also call setIconifiedByDefault(true).

Joseph Earl
  • 23,351
  • 11
  • 76
  • 89
  • 7
    I just tried adding setIconifiedByDefault(true) but it is not called – Giuseppe Jan 29 '13 at 17:24
  • @Joseph Earl: I'm having a similar problem here: http://stackoverflow.com/questions/43702055/android-how-do-i-get-searchview-close-button-to-return-to-search-edittext?noredirect=1#comment74449117_43702055. Any thoughts or ideas on how to fix? – AJW Apr 30 '17 at 02:30
0

seems an old thread already, but I thought I got the same problem API 18 in the first beginning. After googled around, found this thread, another hour read the javadoc tried and errored for something I don't pretend fully understand in javadoc, the following work for me now:

searchView.setIconifiedByDefault(true);

   // OnQueryTextListener
   @Override
   public boolean onQueryTextSubmit(String query) {
      Log.d(tag, "onQueryTextSubmit: " + query);
      return true;
   }

   @Override
   public boolean onQueryTextChange(String query) {
      Log.d(tag, "onQueryTextChange: " + query);
      return true;
   }

   // OnCloseListener
   @Override
   public boolean onClose() {
      Log.w(tag, "onClose: ");
      return false;
   }

I played with true/false a bit, that somehow makes the difference, and it works for me now. Hopefully, it could save someone time.

Sean
  • 2,967
  • 2
  • 29
  • 39
0

It's a workaround but has worked for me

  searchView.setOnQueryTextListener(new android.widget.SearchView.OnQueryTextListener() {

                String lastText;

                @Override
                public boolean onQueryTextChange(final String newText) {
                    if (lastText != null && lastText.length() > 1 && newText.isEmpty()) {
                        // close ctn clicked

                        return true;
                    }
}
Nativ
  • 3,092
  • 6
  • 38
  • 69
0

I encountered this issue while trying to detect the showing/dismissal of the SearchView. I ended up using a different listener and it worked for what I need:

        setOnQueryTextFocusChangeListener { _, hasFocus ->
            if (hasFocus) {
                // SearchView is being shown
            } else {
                // SearchView was dismissed
            }
        }
Psylocke
  • 543
  • 1
  • 3
  • 15
0
searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
  @Override
  public void onViewAttachedToWindow(View v) {
    System.out.println("This is Fired When Search view was Expanded");
  }

  @Override
  public void onViewDetachedFromWindow(View v) {
    System.out.println("This is Fired When Search view was Closed");
  }
});
Elletlar
  • 3,136
  • 7
  • 32
  • 38
-3

There is no console in Android to log to. Instead, use the android logging framework:

Log.d("Test Tag", "Testing.  1, 2, 3...");

See also this question: Why doesn't "System.out.println" work in Android?

Community
  • 1
  • 1
Chris Knight
  • 24,333
  • 24
  • 88
  • 134
  • 2
    Not true, it works just fine. Please see my updated question - I've added a line in the onQueryTextChange method to prove it. Also tried adding Log.d(), but that didn't display anything either. – Michell Bak Feb 17 '12 at 12:06
  • Oh, my bad! Looks like somewhere along the way they've decided to output System.out.println to the Log.i. Good luck – Chris Knight Feb 17 '12 at 14:20
-3

There are two common patterns for SearchView.setOnCloseListener(). This is really true of all listeners, but I'm addressing your question specifically. The first way is to create a listener function and attach it to a member variable, and the second is to make the class implement the interface and have the handler be a member function.

Creating a listener object looks like this:

private SearchView mSearchView;
private final SearchView.OnCloseListener mOnCloseListener = 
    new SearchView.OnCloseListener() {
        public boolean onClose() {
            doStuff();
            return myBooleanResult;
        }
    };
mSearchView.setOnCloseListener(mOnCloseListener);

Implementing listener at class level looks like this:

public class MyClass implements OnCloseListener {
    private SearchView mSearchView;

    public MyClass(...) {
        mSearchView.setOnCloseListener(this);
    }

    @Override
    public boolean onClose() {
        doStuff();
        return false;
    }
}

I have not seen any examples that create the OnCloseListener ad-hoc, as you did in your question.

Sparky
  • 8,437
  • 1
  • 29
  • 41
  • Hey Sparky, thanks for commenting on this. I don't really see how that should change anything. A nested listener is a valid way of making it as well, and it works on Honeycomb as you can see based on the comments here. I haven't had any issues with nested listeners on ICS other than this - which again works on Honeycomb. – Michell Bak Apr 03 '12 at 11:29
  • By nested listeners I mean anonymous inner classes. – Michell Bak Apr 03 '12 at 15:45
  • I agree that it shouldn't matter. I'm only commenting on what's present in the code base. I'll look and see if maybe the conditions in which onClose were redefined. – Sparky Apr 03 '12 at 17:31
  • I'm not sure you understand Java -- it's called an anonymous inner class. – Joseph Earl Nov 28 '12 at 17:06