'Android SearchView.OnQueryTextListener OnQueryTextSubmit not fired on empty query string

I am using Android 4.1.2. I have a SearchView widget on an ActionBar. Documentation on SearchView.OnQueryTextListener from the android developer site states that onQueryTextSubmit is fired/Called when the user submits the query. This could be due to a keypress on the keyboard or due to pressing a submit button."

This does not happen if the search query is empty. I need this to fire on an empty query to clear the search filter of a ListView. Is this a bug or am I doing something wrong?



Solution 1:[1]

It is not a bug, the source code deliberately checks against null and empty values:

private void onSubmitQuery() {
    CharSequence query = mQueryTextView.getText();
    if (query != null && TextUtils.getTrimmedLength(query) > 0) {

However you should be able to use the OnQueryTextChange callback to clear your ListView's filterables when the user clears the search EditText.

Solution 2:[2]

I've an easier work around: use onQueryTextChange, but only render if it's empty.

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

            @Override
            public boolean onQueryTextChange(String newText) {
                    if (searchView.getQuery().length() == 0) {
                            renderList(true);
                    }
                    return false;
            }
    });

Solution 3:[3]

I had the same problem and end up with the following solution: custom SearchView + OnQueryTextListener.onQueryTextChange

Custom SearchView:

public class MySearchView extends SearchView {

private boolean expanded;

public MySearchView(Context context) {
    super(context);
}

@Override
public void onActionViewExpanded() {
    super.onActionViewExpanded();
    expanded = true;
}

@Override
public void onActionViewCollapsed() {
    super.onActionViewCollapsed();
    expanded = false;
}

public boolean isExpanded() {
    return expanded;
}
}

Creating action and setting callback:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    searchAction = menu.add(0, SEARCH_ACTION_ID, 0 , getString(R.string.action_search));
    searchAction.setShowAsAction(SHOW_AS_ACTION_ALWAYS | SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);       
    searchView = new MySearchView(getSherlockActivity());
    searchView.setOnQueryTextListener(searchQueryListener);
    searchView.setIconifiedByDefault(true);
    searchAction.setActionView(searchView);
}

And last the listener:

private OnQueryTextListener searchQueryListener = new OnQueryTextListener() {
    @Override
    public boolean onQueryTextSubmit(String query) {
        search(query);
        return true;
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        if (searchView.isExpanded() && TextUtils.isEmpty(newText)) {
            search("");
        }

        return true;
    }

    public void search(String query) {
        // reset loader, swap cursor, etc.
    }

};

Tested on ABS 4.3.

Solution 4:[4]

I had same issue with SearchView. My solution which consists of styling SearchView as shown in guide http://novoda.com/blog/styling-the-actionbar-searchview/ and setting OnEditorActionListener for EditText( How do I trigger an action when the user has hit enter?) which is part of the SearchView was this:

final SearchView searchView = (SearchView)findViewById(R.id.search);
int searchPlateId = searchView.getContext().getResources().getIdentifier("android:id/search_src_text", null, null);
EditText searchPlate = (EditText) searchView.findViewById(searchPlateId);
searchPlate.setOnEditorActionListener(new TextView.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

        if (actionId == EditorInfo.IME_ACTION_SEARCH) {
            //Do something
        }
        return false;

    }});

Solution 5:[5]

It's an old thread but I've found another solution (Kotlin code, but works in Java as well):

override fun onQueryTextChange(newText: String?): Boolean {
            if(newText == null || newText.isEmpty())
                searchView.setQuery("\u00A0", false)
            return true
        }

\u00A0 is a character that looks like space, but code-wise it is not. In other words SearchView is not empty. Empty string or " " would not work, because base SearchView uses trim, plus user may want to search text ending or beginning with space. But as far as I know users cannot enter \u00A0 character.

Then all you need to do is override getQuery to return super.getQuery.replace("\u00A0", "") in your custom SearchView

Solution 6:[6]

This is a very dirty hack, however it works correctly as expected, enabling the submit action even when there is no text entered (or entered then edited) in the SearchView.

It reflectively gains access to the inner TextView editor action listener, wraps it in a custom listener where it delegates the call to the handler, and finally sets it as the action listener of the inner TextView.

    Class klass = searchView.getClass();
    try {
        Field currentListenerField = klass.getDeclaredField("mOnEditorActionListener");
        currentListenerField.setAccessible(true);
        TextView.OnEditorActionListener previousListener = (TextView.OnEditorActionListener) currentListenerField.get(searchView);
        TextView.OnEditorActionListener newListener = new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (v.getText().length() == 0)
                    handleQuery("");
                return previousListener.onEditorAction(v, actionId, event);
            }
        };
        Field innerTextViewField = klass.getDeclaredField("mSearchSrcTextView");
        innerTextViewField.setAccessible(true);
        SearchView.SearchAutoComplete innerTextView = (SearchView.SearchAutoComplete) innerTextViewField.get(searchView);
        innerTextView.setOnEditorActionListener(newListener);
    } catch (Exception e) {
        throw new IllegalStateException(e);
    }

Solution 7:[7]

As others have mentioned this behavior is intentional. I gave up on a solution for OnQueryChangeListener and decided to workaround by implementing OnEditorActionListener on the SearchView's EditText, which you can get a handle to using R.id.search_src_text. As long as you setImeOptions of the SearchView to EditorInfo.IME_ACTION_SEARCH you can handle a click on the keyboard search button. See this SO answer for more details.

Solution 8:[8]

I can manage to do it simply by implementing the setOnQueryTextListener in this way:

SearchView searchView = (SearchView) menu.findItem(R.id.searchProductView).getActionView();
    searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
        @Override
        public boolean onQueryTextSubmit(String query) {
            return false;
        }

        @Override
        public boolean onQueryTextChange(String newText) {
            if (TextUtils.isEmpty(newText)) {
                loadLocalData(newText);
            }
            return true;
        }
    });

Where my loadLocalData method is

//query my DB
products = ProductDAO.findByIdList(idList);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
listView.setLayoutManager(layoutManager);
listView.setAdapter(<YOURADAPTER>)

This solution clears your query even if you clear the text with the "X" button

Solution 9:[9]

I had the same problem, since empty query's are not supported I had to download and use ActionBarSherlock and then modify the onSubmitQuery() method.

This is how my onSubmitQuery() looks like now

private void onSubmitQuery() {
     CharSequence query = mQueryTextView.getText();
     if (query == null) {query = "";}
     if (mOnQueryChangeListener == null
             || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
         if (mSearchable != null) {
             launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
             setImeVisibility(false);
         }
         dismissSuggestions();
     }
 }

Hope this helps.

Solution 10:[10]

This is a complete example written in Kotlin:

import android.content.Context
import android.text.TextUtils
import android.util.AttributeSet
import android.view.inputmethod.EditorInfo
import android.widget.TextView
import androidx.appcompat.widget.SearchView

/**
 * Custom [SearchView] which can listen to empty submit events
 *
 * If the internal implementation would change, this will not crash at runtime. It will simply
 * ignore the empty submit listener and will continue to work as this is the base [SearchView]
 */
class EmptySubmitSearchView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : SearchView(context, attrs, defStyleAttr) {

    private lateinit var searchAutoComplete: SearchAutoComplete

    private lateinit var internalEditorListener: TextView.OnEditorActionListener

    private var emptySubmitListener: OnEmptySubmitListener? = null

    init {
        try {
            val defaultListenerField = SearchView::class.java
                .getDeclaredField("mOnEditorActionListener")
            defaultListenerField.isAccessible = true
            internalEditorListener = defaultListenerField.get(this) as TextView.OnEditorActionListener

            val searchAutoCompleteField = SearchView::class.java
                .getDeclaredField("mSearchSrcTextView")
            searchAutoCompleteField.isAccessible = true
            searchAutoComplete = searchAutoCompleteField.get(this) as SearchAutoComplete

            searchAutoComplete.setOnEditorActionListener { v, actionId, event ->
                if (actionId == EditorInfo.IME_ACTION_DONE && TextUtils.isEmpty(searchAutoComplete.text)) {
                    emptySubmitListener?.onEmptyTextSubmit()
                } else {
                    internalEditorListener.onEditorAction(v, actionId, event)
                }
                true
            }
        } catch (ignored: Exception) {
            //Some internal field has changed. Check there is a field called
            //mSearchSrcTextView and mOnEditorActionListener and if not update them accordingly
        }
    }

    /**
     * Set custom empty listener or null to remove it
     */
    fun setOnEmptySubmitListener(listener: OnEmptySubmitListener?) {
        emptySubmitListener = listener
    }

    interface OnEmptySubmitListener {
        fun onEmptyTextSubmit()
    }
}

You can set a custom listener with setOnEmptySubmitListener which will enable you to distinguish between events.

You can customize the actionId to respond to, in this case I had in my XML the EmptySubmitSearchView with this android:imeOptions="actionDone|flagNoExtractUi"

Solution 11:[11]

You cannot overwrite this behavior. A workaround could be to clear the filter, when a user exits the searchview.

You could use the OnCloseListener for this. However, this can cause problems as well, depending on the minimum API Level you are developing for.

Solution 12:[12]

Another option is to trigger the onQueryTextChange method manually through a TextWatcher:

public class MyActivity extends Activity implements SearchView.OnQueryTextListener, TextWatcher {

    // Basic onCreate(Bundle) here

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

        final SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
        final SearchManager manager = (SearchManager) getSystemService(SEARCH_SERVICE);
        searchView.setSearchableInfo(manager.getSearchableInfo(getComponentName()));
        searchView.setOnQueryTextListener(this);

        final int resource_edit_text = searchView.getContext().getResources().getIdentifier("android:id/search_src_text", null, null);
        ((EditText) searchView.findViewById(resource_edit_text)).addTextChangedListener(this);

        return super.onCreateOptionsMenu(menu);
    }

    // Implementation of TextWatcher, used to have a proper onQueryTextChange (it doesn't update when the last character is removed)
    @Override
    public void beforeTextChanged (final CharSequence charSequence, final int start, final int count, final int after) {}

    @Override
    public void onTextChanged (final CharSequence charSequence, final int start, final int before, final int after) {}

    @Override
    public void afterTextChanged (final Editable editable) {
        if (editable.length() == 0)
            onQueryTextChange(editable.toString());
    }

    // Implementation of OnQueryTextChangeListener

    @Override
    public boolean onQueryTextChange (final String query) {
        // do stuff
        return false;
    }

    @Override
    public boolean onQueryTextSubmit (final String query) {
        // do stuff
        return false;
    }
}

This all goes with all the default xml files which have been given up here, that is not the point of this answer. This is a workaround that I choose to use, feel free to use others

Solution 13:[13]

In my case I just wanted to enable user to clear his query as it was used as keyword in API search, so the simplest idea was:

    searchView.setOnSearchClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            userQuery = "";
        }
    });

where userQuery is my variable used for searching etc.

Hope it helps someone :)

Solution 14:[14]

Here is samvel1024's answer converted it to Kotlin. You may need to change IME_ACTION_GO to whatever you're using for your keyboard's 'go' button. Simply run this method during your page initialization.

/**
 * Business: User should be allowed to perform an empty search.
 * SearchView does not cause OnQueryTextChange to fire when the box is empty.
 * To create a listener method for this scenario, we use reflection to gain access to the SearchView's inner TextView.
 */
private fun enableEmptySearch(searchView: SearchView) {
    try {
        val searchViewClass = searchView::class.java
        val currentListenerField: Field = searchViewClass.getDeclaredField("mOnEditorActionListener")
        currentListenerField.isAccessible = true
        val previousListener: TextView.OnEditorActionListener = currentListenerField.get(searchView) as TextView.OnEditorActionListener
        val newListener: TextView.OnEditorActionListener = TextView.OnEditorActionListener { v, actionId, event ->
            if (actionId == EditorInfo.IME_ACTION_GO && v.text.isEmpty()) {
                querySubmit()
            }
            previousListener.onEditorAction(v, actionId, event)
        }
        val innerTextViewField: Field = searchViewClass.getDeclaredField("mSearchSrcTextView")
        innerTextViewField.isAccessible = true
        (innerTextViewField.get(searchView) as TextView).setOnEditorActionListener(newListener) // Allow user to tap the "GO" button when the SearchView is empty.
    } catch (ignored: Exception) {
        // If a major change occurs to android SDK, internal fields mSearchSrcTextView or mOnEditorActionListener might not exist.
        // This exception will prevent this additional feature from functioning, but not crash the app or cause any other issues.
    }
}

Solution 15:[15]

For Koltin users:

Simply use the following instead of searchView.setOnQueryTextListener

val searchPlate = searchView.findViewById<EditText>(R.id.search_src_text)
        searchPlate.setOnEditorActionListener { _, actionId, _ ->
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                searchYourItems(searchView.query.toString()) // your code goes here
            }
            false
        }