FileDocCategorySizeDatePackage
SearchView.javaAPI DocAndroid 5.1 API70334Thu Mar 12 22:22:56 GMT 2015android.support.v7.widget

SearchView

public class SearchView extends LinearLayoutCompat implements android.support.v7.view.CollapsibleActionView
A widget that provides a user interface for the user to enter a search query and submit a request to a search provider. Shows a list of query suggestions or results, if available, and allows the user to pick a suggestion or result to launch into.

Note: This class is included in the support library for compatibility with API level 7 and higher. If you're developing your app for API level 11 and higher only, you should instead use the framework {@link android.widget.SearchView} class.

When the SearchView is used in an {@link android.support.v7.app.ActionBar} as an action view, it's collapsed by default, so you must provide an icon for the action.

If you want the search field to always be visible, then call {@link #setIconifiedByDefault(boolean) setIconifiedByDefault(false)}.

Developer Guides

For information about using {@code SearchView}, read the Search API guide. Additional information about action views is also available in the <Action Bar API guide

see
android.support.v4.view.MenuItemCompat#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW

Fields Summary
private static final boolean
DBG
private static final String
LOG_TAG
private static final boolean
IS_AT_LEAST_FROYO
private static final String
IME_OPTION_NO_MICROPHONE
Private constant for removing the microphone in the keyboard.
private final SearchAutoComplete
mQueryTextView
private final android.view.View
mSearchEditFrame
private final android.view.View
mSearchPlate
private final android.view.View
mSubmitArea
private final android.widget.ImageView
mSearchButton
private final android.widget.ImageView
mSubmitButton
private final android.widget.ImageView
mCloseButton
private final android.widget.ImageView
mVoiceButton
private final android.widget.ImageView
mSearchHintIcon
private final android.view.View
mDropDownAnchor
private final int
mSearchIconResId
private final int
mSuggestionRowLayout
private final int
mSuggestionCommitIconResId
private final android.content.Intent
mVoiceWebSearchIntent
private final android.content.Intent
mVoiceAppSearchIntent
private OnQueryTextListener
mOnQueryChangeListener
private OnCloseListener
mOnCloseListener
private OnFocusChangeListener
mOnQueryTextFocusChangeListener
private OnSuggestionListener
mOnSuggestionListener
private OnClickListener
mOnSearchClickListener
private boolean
mIconifiedByDefault
private boolean
mIconified
private android.support.v4.widget.CursorAdapter
mSuggestionsAdapter
private boolean
mSubmitButtonEnabled
private CharSequence
mQueryHint
private boolean
mQueryRefinement
private boolean
mClearingFocus
private int
mMaxWidth
private boolean
mVoiceButtonEnabled
private CharSequence
mOldQueryText
private CharSequence
mUserQuery
private boolean
mExpandedInActionView
private int
mCollapsedImeOptions
private android.app.SearchableInfo
mSearchable
private android.os.Bundle
mAppSearchData
private final android.support.v7.internal.widget.TintManager
mTintManager
static final AutoCompleteTextViewReflector
HIDDEN_METHOD_INVOKER
private Runnable
mShowImeRunnable
private final Runnable
mUpdateDrawableStateRunnable
private Runnable
mReleaseCursorRunnable
private final WeakHashMap
mOutsideDrawablesCache
private final OnClickListener
mOnClickListener
View.OnKeyListener
mTextKeyListener
React to the user typing "enter" or other hardwired keys while typing in the search box. This handles these special keys while the edit box has focus.
private final android.widget.TextView.OnEditorActionListener
mOnEditorActionListener
private final android.widget.AdapterView.OnItemClickListener
mOnItemClickListener
private final android.widget.AdapterView.OnItemSelectedListener
mOnItemSelectedListener
private android.text.TextWatcher
mTextWatcher
Callback to watch the text field for empty/non-empty
Constructors Summary
public SearchView(android.content.Context context)


                
       

                                                                                                          
          

                                                              
          
    

       

                                              
         
    

                              
       

                                                                
          

                                                                    
          
    

       
        this(context, null);
    
public SearchView(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, R.attr.searchViewStyle);
    
public SearchView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        super(context, attrs, defStyleAttr);

        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(context,
                attrs, R.styleable.SearchView, defStyleAttr, 0);
        // Keep the TintManager in case we need it later
        mTintManager = a.getTintManager();

        final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        final int layoutResId = a.getResourceId(R.styleable.SearchView_layout, 0);
        inflater.inflate(layoutResId, this, true);
        mQueryTextView = (SearchAutoComplete) findViewById(R.id.search_src_text);
        mQueryTextView.setSearchView(this);

        mSearchEditFrame = findViewById(R.id.search_edit_frame);
        mSearchPlate = findViewById(R.id.search_plate);
        mSubmitArea = findViewById(R.id.submit_area);
        mSearchButton = (ImageView) findViewById(R.id.search_button);
        mSubmitButton = (ImageView) findViewById(R.id.search_go_btn);
        mCloseButton = (ImageView) findViewById(R.id.search_close_btn);
        mVoiceButton = (ImageView) findViewById(R.id.search_voice_btn);
        mSearchHintIcon = (ImageView) findViewById(R.id.search_mag_icon);
        // Set up icons and backgrounds.
        mSearchPlate.setBackgroundDrawable(a.getDrawable(R.styleable.SearchView_queryBackground));
        mSubmitArea.setBackgroundDrawable(a.getDrawable(R.styleable.SearchView_submitBackground));
        mSearchIconResId = a.getResourceId(R.styleable.SearchView_searchIcon, 0);
        mSearchButton.setImageResource(mSearchIconResId);
        mSubmitButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_goIcon));
        mCloseButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_closeIcon));
        mVoiceButton.setImageDrawable(a.getDrawable(R.styleable.SearchView_voiceIcon));
        mSearchHintIcon.setImageDrawable(a.getDrawable(R.styleable.SearchView_searchIcon));

        // Extract dropdown layout resource IDs for later use.
        mSuggestionRowLayout = a.getResourceId(R.styleable.SearchView_suggestionRowLayout, 0);
        mSuggestionCommitIconResId = a.getResourceId(R.styleable.SearchView_commitIcon, 0);

        mSearchButton.setOnClickListener(mOnClickListener);
        mCloseButton.setOnClickListener(mOnClickListener);
        mSubmitButton.setOnClickListener(mOnClickListener);
        mVoiceButton.setOnClickListener(mOnClickListener);
        mQueryTextView.setOnClickListener(mOnClickListener);

        mQueryTextView.addTextChangedListener(mTextWatcher);
        mQueryTextView.setOnEditorActionListener(mOnEditorActionListener);
        mQueryTextView.setOnItemClickListener(mOnItemClickListener);
        mQueryTextView.setOnItemSelectedListener(mOnItemSelectedListener);
        mQueryTextView.setOnKeyListener(mTextKeyListener);
        // Inform any listener of focus changes
        mQueryTextView.setOnFocusChangeListener(new OnFocusChangeListener() {

            public void onFocusChange(View v, boolean hasFocus) {
                if (mOnQueryTextFocusChangeListener != null) {
                    mOnQueryTextFocusChangeListener.onFocusChange(SearchView.this, hasFocus);
                }
            }
        });
        setIconifiedByDefault(a.getBoolean(R.styleable.SearchView_iconifiedByDefault, true));

        final int maxWidth = a.getDimensionPixelSize(R.styleable.SearchView_android_maxWidth, -1);
        if (maxWidth != -1) {
            setMaxWidth(maxWidth);
        }
        final CharSequence queryHint = a.getText(R.styleable.SearchView_queryHint);
        if (!TextUtils.isEmpty(queryHint)) {
            setQueryHint(queryHint);
        }
        final int imeOptions = a.getInt(R.styleable.SearchView_android_imeOptions, -1);
        if (imeOptions != -1) {
            setImeOptions(imeOptions);
        }
        final int inputType = a.getInt(R.styleable.SearchView_android_inputType, -1);
        if (inputType != -1) {
            setInputType(inputType);
        }

        boolean focusable = true;
        focusable = a.getBoolean(R.styleable.SearchView_android_focusable, focusable);
        setFocusable(focusable);

        a.recycle();

        // Save voice intent for later queries/launching
        mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH);
        mVoiceWebSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mVoiceWebSearchIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);

        mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor());
        if (mDropDownAnchor != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                addOnLayoutChangeListenerToDropDownAnchorSDK11();
            } else {
                addOnLayoutChangeListenerToDropDownAnchorBase();
            }
        }

        updateViewsVisibility(mIconifiedByDefault);
        updateQueryHint();
    
Methods Summary
private voidaddOnLayoutChangeListenerToDropDownAnchorBase()

        mDropDownAnchor.getViewTreeObserver()
                .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        adjustDropDownSizeAndPosition();
                    }
                });
    
private voidaddOnLayoutChangeListenerToDropDownAnchorSDK11()

        mDropDownAnchor.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom,
                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
                adjustDropDownSizeAndPosition();
            }
        });
    
private voidadjustDropDownSizeAndPosition()

        if (mDropDownAnchor.getWidth() > 1) {
            Resources res = getContext().getResources();
            int anchorPadding = mSearchPlate.getPaddingLeft();
            Rect dropDownPadding = new Rect();
            final boolean isLayoutRtl = ViewUtils.isLayoutRtl(this);
            int iconOffset = mIconifiedByDefault
                    ? res.getDimensionPixelSize(R.dimen.abc_dropdownitem_icon_width)
                    + res.getDimensionPixelSize(R.dimen.abc_dropdownitem_text_padding_left)
                    : 0;
            mQueryTextView.getDropDownBackground().getPadding(dropDownPadding);
            int offset;
            if (isLayoutRtl) {
                offset = - dropDownPadding.left;
            } else {
                offset = anchorPadding - (dropDownPadding.left + iconOffset);
            }
            mQueryTextView.setDropDownHorizontalOffset(offset);
            final int width = mDropDownAnchor.getWidth() + dropDownPadding.left
                    + dropDownPadding.right + iconOffset - anchorPadding;
            mQueryTextView.setDropDownWidth(width);
        }
    
public voidclearFocus()

hide

        mClearingFocus = true;
        setImeVisibility(false);
        super.clearFocus();
        mQueryTextView.clearFocus();
        mClearingFocus = false;
    
private android.content.IntentcreateIntent(java.lang.String action, android.net.Uri data, java.lang.String extraData, java.lang.String query, int actionKey, java.lang.String actionMsg)
Constructs an intent from the given information and the search dialog state.

param
action Intent action.
param
data Intent data, or null.
param
extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or null.
param
query Intent query, or null.
param
actionKey The key code of the action key that was pressed, or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
param
actionMsg The message for the action key that was pressed, or null if none.
return
The intent.

        // Now build the Intent
        Intent intent = new Intent(action);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        // We need CLEAR_TOP to avoid reusing an old task that has other activities
        // on top of the one we want. We don't want to do this in in-app search though,
        // as it can be destructive to the activity stack.
        if (data != null) {
            intent.setData(data);
        }
        intent.putExtra(SearchManager.USER_QUERY, mUserQuery);
        if (query != null) {
            intent.putExtra(SearchManager.QUERY, query);
        }
        if (extraData != null) {
            intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
        }
        if (mAppSearchData != null) {
            intent.putExtra(SearchManager.APP_DATA, mAppSearchData);
        }
        if (actionKey != KeyEvent.KEYCODE_UNKNOWN) {
            intent.putExtra(SearchManager.ACTION_KEY, actionKey);
            intent.putExtra(SearchManager.ACTION_MSG, actionMsg);
        }
        if (IS_AT_LEAST_FROYO) {
            intent.setComponent(mSearchable.getSearchActivity());
        }
        return intent;
    
private android.content.IntentcreateIntentFromSuggestion(android.database.Cursor c, int actionKey, java.lang.String actionMsg)
When a particular suggestion has been selected, perform the various lookups required to use the suggestion. This includes checking the cursor for suggestion-specific data, and/or falling back to the XML for defaults; It also creates REST style Uri data when the suggestion includes a data id.

param
c The suggestions cursor, moved to the row of the user's selection
param
actionKey The key code of the action key that was pressed, or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
param
actionMsg The message for the action key that was pressed, or null if none.
return
An intent for the suggestion at the cursor's position.

        try {
            // use specific action if supplied, or default action if supplied, or fixed default
            String action = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_ACTION);

            if (action == null && Build.VERSION.SDK_INT >= 8) {
                action = mSearchable.getSuggestIntentAction();
            }
            if (action == null) {
                action = Intent.ACTION_SEARCH;
            }

            // use specific data if supplied, or default data if supplied
            String data = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA);
            if (IS_AT_LEAST_FROYO && data == null) {
                data = mSearchable.getSuggestIntentData();
            }
            // then, if an ID was provided, append it.
            if (data != null) {
                String id = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
                if (id != null) {
                    data = data + "/" + Uri.encode(id);
                }
            }
            Uri dataUri = (data == null) ? null : Uri.parse(data);

            String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY);
            String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);

            return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
        } catch (RuntimeException e ) {
            int rowNum;
            try {                       // be really paranoid now
                rowNum = c.getPosition();
            } catch (RuntimeException e2 ) {
                rowNum = -1;
            }
            Log.w(LOG_TAG, "Search suggestions cursor at row " + rowNum +
                    " returned exception.", e);
            return null;
        }
    
private android.content.IntentcreateVoiceAppSearchIntent(android.content.Intent baseIntent, android.app.SearchableInfo searchable)
Create and return an Intent that can launch the voice search activity, perform a specific voice transcription, and forward the results to the searchable activity.

param
baseIntent The voice app search intent to start from
return
A completely-configured intent ready to send to the voice search activity

        ComponentName searchActivity = searchable.getSearchActivity();

        // create the necessary intent to set up a search-and-forward operation
        // in the voice search system.   We have to keep the bundle separate,
        // because it becomes immutable once it enters the PendingIntent
        Intent queryIntent = new Intent(Intent.ACTION_SEARCH);
        queryIntent.setComponent(searchActivity);
        PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent,
                PendingIntent.FLAG_ONE_SHOT);

        // Now set up the bundle that will be inserted into the pending intent
        // when it's time to do the search.  We always build it here (even if empty)
        // because the voice search activity will always need to insert "QUERY" into
        // it anyway.
        Bundle queryExtras = new Bundle();
        if (mAppSearchData != null) {
            queryExtras.putParcelable(SearchManager.APP_DATA, mAppSearchData);
        }

        // Now build the intent to launch the voice search.  Add all necessary
        // extras to launch the voice recognizer, and then all the necessary extras
        // to forward the results to the searchable activity
        Intent voiceIntent = new Intent(baseIntent);

        // Add all of the configuration options supplied by the searchable's metadata
        String languageModel = RecognizerIntent.LANGUAGE_MODEL_FREE_FORM;
        String prompt = null;
        String language = null;
        int maxResults = 1;

        if (Build.VERSION.SDK_INT >= 8) {
            Resources resources = getResources();
            if (searchable.getVoiceLanguageModeId() != 0) {
                languageModel = resources.getString(searchable.getVoiceLanguageModeId());
            }
            if (searchable.getVoicePromptTextId() != 0) {
                prompt = resources.getString(searchable.getVoicePromptTextId());
            }
            if (searchable.getVoiceLanguageId() != 0) {
                language = resources.getString(searchable.getVoiceLanguageId());
            }
            if (searchable.getVoiceMaxResults() != 0) {
                maxResults = searchable.getVoiceMaxResults();
            }
        }
        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel);
        voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
        voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language);
        voiceIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, maxResults);
        voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null
                : searchActivity.flattenToShortString());

        // Add the values that configure forwarding the results
        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pending);
        voiceIntent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, queryExtras);

        return voiceIntent;
    
private android.content.IntentcreateVoiceWebSearchIntent(android.content.Intent baseIntent, android.app.SearchableInfo searchable)
Create and return an Intent that can launch the voice search activity for web search.

        Intent voiceIntent = new Intent(baseIntent);
        ComponentName searchActivity = searchable.getSearchActivity();
        voiceIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, searchActivity == null ? null
                : searchActivity.flattenToShortString());
        return voiceIntent;
    
private voiddismissSuggestions()

        mQueryTextView.dismissDropDown();
    
private voidforceSuggestionQuery()

        HIDDEN_METHOD_INVOKER.doBeforeTextChanged(mQueryTextView);
        HIDDEN_METHOD_INVOKER.doAfterTextChanged(mQueryTextView);
    
private java.lang.CharSequencegetDecoratedHint(java.lang.CharSequence hintText)

        // If the field is always expanded, then don't add the search icon to the hint
        if (!mIconifiedByDefault) {
            return hintText;
        }

        final Drawable searchIcon = mTintManager.getDrawable(mSearchIconResId);
        final int textSize = (int) (mQueryTextView.getTextSize() * 1.25);
        searchIcon.setBounds(0, 0, textSize, textSize);

        final SpannableStringBuilder ssb = new SpannableStringBuilder("   "); // for the icon
        ssb.append(hintText);
        ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        return ssb;
    
public intgetImeOptions()
Returns the IME options set on the query text field.

return
the ime options
see
TextView#setImeOptions(int)

        return mQueryTextView.getImeOptions();
    
public intgetInputType()
Returns the input type set on the query text field.

return
the input type

        return mQueryTextView.getInputType();
    
public intgetMaxWidth()
Gets the specified maximum width in pixels, if set. Returns zero if no maximum width was specified.

return
the maximum width of the view

        return mMaxWidth;
    
private intgetPreferredWidth()

        return getContext().getResources()
                .getDimensionPixelSize(R.dimen.abc_search_view_preferred_width);
    
public java.lang.CharSequencegetQuery()
Returns the query string currently in the text field.

return
the query string

        return mQueryTextView.getText();
    
public java.lang.CharSequencegetQueryHint()
Gets the hint text to display in the query text field.

return
the query hint text, if specified, null otherwise.

        if (mQueryHint != null) {
            return mQueryHint;
        } else if (IS_AT_LEAST_FROYO && mSearchable != null) {
            CharSequence hint = null;
            int hintId = mSearchable.getHintId();
            if (hintId != 0) {
                hint = getContext().getString(hintId);
            }
            return hint;
        }
        return null;
    
intgetSuggestionCommitIconResId()

        return mSuggestionCommitIconResId;
    
intgetSuggestionRowLayout()

        return mSuggestionRowLayout;
    
public android.support.v4.widget.CursorAdaptergetSuggestionsAdapter()
Returns the adapter used for suggestions, if any.

return
the suggestions adapter

        return mSuggestionsAdapter;
    
private booleanhasVoiceSearch()

        if (mSearchable != null &&
                mSearchable.getVoiceSearchEnabled()) {
            Intent testIntent = null;
            if (mSearchable.getVoiceSearchLaunchWebSearch()) {
                testIntent = mVoiceWebSearchIntent;
            } else if (mSearchable.getVoiceSearchLaunchRecognizer()) {
                testIntent = mVoiceAppSearchIntent;
            }
            if (testIntent != null) {
                ResolveInfo ri = getContext().getPackageManager().resolveActivity(testIntent,
                        PackageManager.MATCH_DEFAULT_ONLY);
                return ri != null;
            }
        }
        return false;
    
public booleanisIconfiedByDefault()
Returns the default iconified state of the search field.

return

        return mIconifiedByDefault;
    
public booleanisIconified()
Returns the current iconified state of the SearchView.

return
true if the SearchView is currently iconified, false if the search field is fully visible.

        return mIconified;
    
static booleanisLandscapeMode(android.content.Context context)

        return context.getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE;
    
public booleanisQueryRefinementEnabled()
Returns whether query refinement is enabled for all items or only specific ones.

return
true if enabled for all items, false otherwise.

        return mQueryRefinement;
    
private booleanisSubmitAreaEnabled()

        return (mSubmitButtonEnabled || mVoiceButtonEnabled) && !isIconified();
    
public booleanisSubmitButtonEnabled()
Returns whether the submit button is enabled when necessary or never displayed.

return
whether the submit button is enabled automatically when necessary

        return mSubmitButtonEnabled;
    
private voidlaunchIntent(android.content.Intent intent)
Launches an intent, including any special intent handling.

        if (intent == null) {
            return;
        }
        try {
            // If the intent was created from a suggestion, it will always have an explicit
            // component here.
            getContext().startActivity(intent);
        } catch (RuntimeException ex) {
            Log.e(LOG_TAG, "Failed launch activity: " + intent, ex);
        }
    
private voidlaunchQuerySearch(int actionKey, java.lang.String actionMsg, java.lang.String query)

        String action = Intent.ACTION_SEARCH;
        Intent intent = createIntent(action, null, null, query, actionKey, actionMsg);
        getContext().startActivity(intent);
    
private booleanlaunchSuggestion(int position, int actionKey, java.lang.String actionMsg)
Launches an intent based on a suggestion.

param
position The index of the suggestion to create the intent from.
param
actionKey The key code of the action key that was pressed, or {@link KeyEvent#KEYCODE_UNKNOWN} if none.
param
actionMsg The message for the action key that was pressed, or null if none.
return
true if a successful launch, false if could not (e.g. bad position).

        Cursor c = mSuggestionsAdapter.getCursor();
        if ((c != null) && c.moveToPosition(position)) {

            Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg);

            // launch the intent
            launchIntent(intent);

            return true;
        }
        return false;
    
public voidonActionViewCollapsed()
{@inheritDoc}

        setQuery("", false);
        clearFocus();
        updateViewsVisibility(true);
        mQueryTextView.setImeOptions(mCollapsedImeOptions);
        mExpandedInActionView = false;
    
public voidonActionViewExpanded()
{@inheritDoc}

        if (mExpandedInActionView) return;

        mExpandedInActionView = true;
        mCollapsedImeOptions = mQueryTextView.getImeOptions();
        mQueryTextView.setImeOptions(mCollapsedImeOptions | EditorInfo.IME_FLAG_NO_FULLSCREEN);
        mQueryTextView.setText("");
        setIconified(false);
    
private voidonCloseClicked()

        CharSequence text = mQueryTextView.getText();
        if (TextUtils.isEmpty(text)) {
            if (mIconifiedByDefault) {
                // If the app doesn't override the close behavior
                if (mOnCloseListener == null || !mOnCloseListener.onClose()) {
                    // hide the keyboard and remove focus
                    clearFocus();
                    // collapse the search field
                    updateViewsVisibility(true);
                }
            }
        } else {
            mQueryTextView.setText("");
            mQueryTextView.requestFocus();
            setImeVisibility(true);
        }

    
protected voidonDetachedFromWindow()

        removeCallbacks(mUpdateDrawableStateRunnable);
        post(mReleaseCursorRunnable);
        super.onDetachedFromWindow();
    
private booleanonItemClicked(int position, int actionKey, java.lang.String actionMsg)

        if (mOnSuggestionListener == null
                || !mOnSuggestionListener.onSuggestionClick(position)) {
            launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
            setImeVisibility(false);
            dismissSuggestions();
            return true;
        }
        return false;
    
private booleanonItemSelected(int position)

        if (mOnSuggestionListener == null
                || !mOnSuggestionListener.onSuggestionSelect(position)) {
            rewriteQueryFromSuggestion(position);
            return true;
        }
        return false;
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        // Let the standard measurements take effect in iconified state.
        if (isIconified()) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);

        switch (widthMode) {
            case MeasureSpec.AT_MOST:
                // If there is an upper limit, don't exceed maximum width (explicit or implicit)
                if (mMaxWidth > 0) {
                    width = Math.min(mMaxWidth, width);
                } else {
                    width = Math.min(getPreferredWidth(), width);
                }
                break;
            case MeasureSpec.EXACTLY:
                // If an exact width is specified, still don't exceed any specified maximum width
                if (mMaxWidth > 0) {
                    width = Math.min(mMaxWidth, width);
                }
                break;
            case MeasureSpec.UNSPECIFIED:
                // Use maximum width, if specified, else preferred width
                width = mMaxWidth > 0 ? mMaxWidth : getPreferredWidth();
                break;
        }
        widthMode = MeasureSpec.EXACTLY;
        super.onMeasure(MeasureSpec.makeMeasureSpec(width, widthMode), heightMeasureSpec);
    
voidonQueryRefine(java.lang.CharSequence queryText)
Called by the SuggestionsAdapter

hide

        setQuery(queryText);
    
private voidonSearchClicked()

        updateViewsVisibility(false);
        mQueryTextView.requestFocus();
        setImeVisibility(true);
        if (mOnSearchClickListener != null) {
            mOnSearchClickListener.onClick(this);
        }
    
private voidonSubmitQuery()

        CharSequence query = mQueryTextView.getText();
        if (query != null && TextUtils.getTrimmedLength(query) > 0) {
            if (mOnQueryChangeListener == null
                    || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
                if (mSearchable != null) {
                    launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
                }
                setImeVisibility(false);
                dismissSuggestions();
            }
        }
    
private booleanonSuggestionsKey(android.view.View v, int keyCode, android.view.KeyEvent event)
React to the user typing while in the suggestions list. First, check for action keys. If not handled, try refocusing regular characters into the EditText.


                                  
            
        // guard against possible race conditions (late arrival after dismiss)
        if (mSearchable == null) {
            return false;
        }
        if (mSuggestionsAdapter == null) {
            return false;
        }
        if (event.getAction() == KeyEvent.ACTION_DOWN && KeyEventCompat.hasNoModifiers(event)) {
            // First, check for enter or search (both of which we'll treat as a
            // "click")
            if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_SEARCH
                    || keyCode == KeyEvent.KEYCODE_TAB) {
                int position = mQueryTextView.getListSelection();
                return onItemClicked(position, KeyEvent.KEYCODE_UNKNOWN, null);
            }

            // Next, check for left/right moves, which we use to "return" the
            // user to the edit view
            if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
                // give "focus" to text editor, with cursor at the beginning if
                // left key, at end if right key
                // TODO: Reverse left/right for right-to-left languages, e.g.
                // Arabic
                int selPoint = (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) ? 0 : mQueryTextView
                        .length();
                mQueryTextView.setSelection(selPoint);
                mQueryTextView.setListSelection(0);
                mQueryTextView.clearListSelection();
                HIDDEN_METHOD_INVOKER.ensureImeVisible(mQueryTextView, true);

                return true;
            }

            // Next, check for an "up and out" move
            if (keyCode == KeyEvent.KEYCODE_DPAD_UP && 0 == mQueryTextView.getListSelection()) {
                // TODO: restoreUserQuery();
                // let ACTV complete the move
                return false;
            }
        }
        return false;
    
private voidonTextChanged(java.lang.CharSequence newText)


        
        CharSequence text = mQueryTextView.getText();
        mUserQuery = text;
        boolean hasText = !TextUtils.isEmpty(text);
        updateSubmitButton(hasText);
        updateVoiceButton(!hasText);
        updateCloseButton();
        updateSubmitArea();
        if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)) {
            mOnQueryChangeListener.onQueryTextChange(newText.toString());
        }
        mOldQueryText = newText.toString();
    
voidonTextFocusChanged()

        updateViewsVisibility(isIconified());
        // Delayed update to make sure that the focus has settled down and window focus changes
        // don't affect it. A synchronous update was not working.
        postUpdateFocusedState();
        if (mQueryTextView.hasFocus()) {
            forceSuggestionQuery();
        }
    
private voidonVoiceClicked()

        // guard against possible race conditions
        if (mSearchable == null) {
            return;
        }
        SearchableInfo searchable = mSearchable;
        try {
            if (searchable.getVoiceSearchLaunchWebSearch()) {
                Intent webSearchIntent = createVoiceWebSearchIntent(mVoiceWebSearchIntent,
                        searchable);
                getContext().startActivity(webSearchIntent);
            } else if (searchable.getVoiceSearchLaunchRecognizer()) {
                Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent,
                        searchable);
                getContext().startActivity(appSearchIntent);
            }
        } catch (ActivityNotFoundException e) {
            // Should not happen, since we check the availability of
            // voice search before showing the button. But just in case...
            Log.w(LOG_TAG, "Could not find voice search activity");
        }
    
public voidonWindowFocusChanged(boolean hasWindowFocus)

        super.onWindowFocusChanged(hasWindowFocus);

        postUpdateFocusedState();
    
private voidpostUpdateFocusedState()

        post(mUpdateDrawableStateRunnable);
    
public booleanrequestFocus(int direction, android.graphics.Rect previouslyFocusedRect)

hide

        // Don't accept focus if in the middle of clearing focus
        if (mClearingFocus) return false;
        // Check if SearchView is focusable.
        if (!isFocusable()) return false;
        // If it is not iconified, then give the focus to the text field
        if (!isIconified()) {
            boolean result = mQueryTextView.requestFocus(direction, previouslyFocusedRect);
            if (result) {
                updateViewsVisibility(false);
            }
            return result;
        } else {
            return super.requestFocus(direction, previouslyFocusedRect);
        }
    
private voidrewriteQueryFromSuggestion(int position)
Query rewriting.


           
        
        CharSequence oldQuery = mQueryTextView.getText();
        Cursor c = mSuggestionsAdapter.getCursor();
        if (c == null) {
            return;
        }
        if (c.moveToPosition(position)) {
            // Get the new query from the suggestion.
            CharSequence newQuery = mSuggestionsAdapter.convertToString(c);
            if (newQuery != null) {
                // The suggestion rewrites the query.
                // Update the text field, without getting new suggestions.
                setQuery(newQuery);
            } else {
                // The suggestion does not rewrite the query, restore the user's query.
                setQuery(oldQuery);
            }
        } else {
            // We got a bad position, restore the user's query.
            setQuery(oldQuery);
        }
    
public voidsetAppSearchData(android.os.Bundle appSearchData)
Sets the APP_DATA for legacy SearchDialog use.

param
appSearchData bundle provided by the app when launching the search dialog
hide

        mAppSearchData = appSearchData;
    
public voidsetIconified(boolean iconify)
Iconifies or expands the SearchView. Any query text is cleared when iconified. This is a temporary state and does not override the default iconified state set by {@link #setIconifiedByDefault(boolean)}. If the default state is iconified, then a false here will only be valid until the user closes the field. And if the default state is expanded, then a true here will only clear the text field and not close it.

param
iconify a true value will collapse the SearchView to an icon, while a false will expand it.

        if (iconify) {
            onCloseClicked();
        } else {
            onSearchClicked();
        }
    
public voidsetIconifiedByDefault(boolean iconified)
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. Changes to this property will take effect immediately.

The default value is true.

param
iconified whether the search field should be iconified by default

        if (mIconifiedByDefault == iconified) return;
        mIconifiedByDefault = iconified;
        updateViewsVisibility(iconified);
        updateQueryHint();
    
public voidsetImeOptions(int imeOptions)
Sets the IME options on the query text field.

see
TextView#setImeOptions(int)
param
imeOptions the options to set on the query text field

        mQueryTextView.setImeOptions(imeOptions);
    
private voidsetImeVisibility(boolean visible)

        if (visible) {
            post(mShowImeRunnable);
        } else {
            removeCallbacks(mShowImeRunnable);
            InputMethodManager imm = (InputMethodManager)
                    getContext().getSystemService(Context.INPUT_METHOD_SERVICE);

            if (imm != null) {
                imm.hideSoftInputFromWindow(getWindowToken(), 0);
            }
        }
    
public voidsetInputType(int inputType)
Sets the input type on the query text field.

see
TextView#setInputType(int)
param
inputType the input type to set on the query text field

        mQueryTextView.setInputType(inputType);
    
public voidsetMaxWidth(int maxpixels)
Makes the view at most this many pixels wide

        mMaxWidth = maxpixels;

        requestLayout();
    
public voidsetOnCloseListener(android.support.v7.widget.SearchView$OnCloseListener listener)
Sets a listener to inform when the user closes the SearchView.

param
listener the listener to call when the user closes the SearchView.

        mOnCloseListener = listener;
    
public voidsetOnQueryTextFocusChangeListener(OnFocusChangeListener listener)
Sets a listener to inform when the focus of the query text field changes.

param
listener the listener to inform of focus changes.

        mOnQueryTextFocusChangeListener = listener;
    
public voidsetOnQueryTextListener(android.support.v7.widget.SearchView$OnQueryTextListener listener)
Sets a listener for user actions within the SearchView.

param
listener the listener object that receives callbacks when the user performs actions in the SearchView such as clicking on buttons or typing a query.

        mOnQueryChangeListener = listener;
    
public voidsetOnSearchClickListener(OnClickListener listener)
Sets a listener to inform when the search button is pressed. This is only relevant when the text field is not visible by default. Calling {@link #setIconified setIconified(false)} can also cause this listener to be informed.

param
listener the listener to inform when the search button is clicked or the text field is programmatically de-iconified.

        mOnSearchClickListener = listener;
    
public voidsetOnSuggestionListener(android.support.v7.widget.SearchView$OnSuggestionListener listener)
Sets a listener to inform when a suggestion is focused or clicked.

param
listener the listener to inform of suggestion selection events.

        mOnSuggestionListener = listener;
    
public voidsetQuery(java.lang.CharSequence query, boolean submit)
Sets a query string in the text field and optionally submits the query as well.

param
query the query string. This replaces any query text already present in the text field.
param
submit whether to submit the query right now or only update the contents of text field.

        mQueryTextView.setText(query);
        if (query != null) {
            mQueryTextView.setSelection(mQueryTextView.length());
            mUserQuery = query;
        }

        // If the query is not empty and submit is requested, submit the query
        if (submit && !TextUtils.isEmpty(query)) {
            onSubmitQuery();
        }
    
private voidsetQuery(java.lang.CharSequence query)
Sets the text in the query box, without updating the suggestions.

        mQueryTextView.setText(query);
        // Move the cursor to the end
        mQueryTextView.setSelection(TextUtils.isEmpty(query) ? 0 : query.length());
    
public voidsetQueryHint(java.lang.CharSequence hint)
Sets the hint text to display in the query text field. This overrides any hint specified in the SearchableInfo.

param
hint the hint text to display

        mQueryHint = hint;
        updateQueryHint();
    
public voidsetQueryRefinementEnabled(boolean enable)
Specifies if a query refinement button should be displayed alongside each suggestion or if it should depend on the flags set in the individual items retrieved from the suggestions provider. Clicking on the query refinement button will replace the text in the query text field with the text from the suggestion. This flag only takes effect if a SearchableInfo has been specified with {@link #setSearchableInfo(SearchableInfo)} and not when using a custom adapter.

param
enable true if all items should have a query refinement button, false if only those items that have a query refinement flag set should have the button.
see
SearchManager#SUGGEST_COLUMN_FLAGS
see
SearchManager#FLAG_QUERY_REFINEMENT

        mQueryRefinement = enable;
        if (mSuggestionsAdapter instanceof SuggestionsAdapter) {
            ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
                    enable ? SuggestionsAdapter.REFINE_ALL : SuggestionsAdapter.REFINE_BY_ENTRY);
        }
    
public voidsetSearchableInfo(android.app.SearchableInfo searchable)
Sets the SearchableInfo for this SearchView. Properties in the SearchableInfo are used to display labels, hints, suggestions, create intents for launching search results screens and controlling other affordances such as a voice button.

param
searchable a SearchableInfo can be retrieved from the SearchManager, for a specific activity or a global search provider.

        mSearchable = searchable;
        if (mSearchable != null) {
            if (IS_AT_LEAST_FROYO) {
                updateSearchAutoComplete();
            }
            updateQueryHint();
        }
        // Cache the voice search capability
        mVoiceButtonEnabled = IS_AT_LEAST_FROYO && hasVoiceSearch();

        if (mVoiceButtonEnabled) {
            // Disable the microphone on the keyboard, as a mic is displayed near the text box
            // TODO: use imeOptions to disable voice input when the new API will be available
            mQueryTextView.setPrivateImeOptions(IME_OPTION_NO_MICROPHONE);
        }
        updateViewsVisibility(isIconified());
    
public voidsetSubmitButtonEnabled(boolean enabled)
Enables showing a submit button when the query is non-empty. In cases where the SearchView is being used to filter the contents of the current activity and doesn't launch a separate results activity, then the submit button should be disabled.

param
enabled true to show a submit button for submitting queries, false if a submit button is not required.

        mSubmitButtonEnabled = enabled;
        updateViewsVisibility(isIconified());
    
public voidsetSuggestionsAdapter(android.support.v4.widget.CursorAdapter adapter)
You can set a custom adapter if you wish. Otherwise the default adapter is used to display the suggestions from the suggestions provider associated with the SearchableInfo.

see
#setSearchableInfo(SearchableInfo)

        mSuggestionsAdapter = adapter;

        mQueryTextView.setAdapter(mSuggestionsAdapter);
    
private voidupdateCloseButton()

        final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());
        // Should we show the close button? It is not shown if there's no focus,
        // field is not iconified by default and there is no text in it.
        final boolean showClose = hasText || (mIconifiedByDefault && !mExpandedInActionView);
        mCloseButton.setVisibility(showClose ? VISIBLE : GONE);
        mCloseButton.getDrawable().setState(hasText ? ENABLED_STATE_SET : EMPTY_STATE_SET);
    
private voidupdateFocusedState()

        boolean focused = mQueryTextView.hasFocus();
        mSearchPlate.getBackground().setState(focused ? ENABLED_FOCUSED_STATE_SET : EMPTY_STATE_SET);
        mSubmitArea.getBackground().setState(focused ? ENABLED_FOCUSED_STATE_SET : EMPTY_STATE_SET);
        invalidate();
    
private voidupdateQueryHint()

        if (mQueryHint != null) {
            mQueryTextView.setHint(getDecoratedHint(mQueryHint));
        } else if (IS_AT_LEAST_FROYO && mSearchable != null) {
            CharSequence hint = null;
            int hintId = mSearchable.getHintId();
            if (hintId != 0) {
                hint = getContext().getString(hintId);
            }
            if (hint != null) {
                mQueryTextView.setHint(getDecoratedHint(hint));
            }
        } else {
            mQueryTextView.setHint(getDecoratedHint(""));
        }
    
private voidupdateSearchAutoComplete()
Updates the auto-complete text view.

        mQueryTextView.setThreshold(mSearchable.getSuggestThreshold());
        mQueryTextView.setImeOptions(mSearchable.getImeOptions());
        int inputType = mSearchable.getInputType();
        // We only touch this if the input type is set up for text (which it almost certainly
        // should be, in the case of search!)
        if ((inputType & InputType.TYPE_MASK_CLASS) == InputType.TYPE_CLASS_TEXT) {
            // The existence of a suggestions authority is the proxy for "suggestions
            // are available here"
            inputType &= ~InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
            if (mSearchable.getSuggestAuthority() != null) {
                inputType |= InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
                // TYPE_TEXT_FLAG_AUTO_COMPLETE means that the text editor is performing
                // auto-completion based on its own semantics, which it will present to the user
                // as they type. This generally means that the input method should not show its
                // own candidates, and the spell checker should not be in action. The text editor
                // supplies its candidates by calling InputMethodManager.displayCompletions(),
                // which in turn will call InputMethodSession.displayCompletions().
                inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
            }
        }
        mQueryTextView.setInputType(inputType);
        if (mSuggestionsAdapter != null) {
            mSuggestionsAdapter.changeCursor(null);
        }
        // attach the suggestions adapter, if suggestions are available
        // The existence of a suggestions authority is the proxy for "suggestions available here"
        if (mSearchable.getSuggestAuthority() != null) {
            mSuggestionsAdapter = new SuggestionsAdapter(getContext(),
                    this, mSearchable, mOutsideDrawablesCache);
            mQueryTextView.setAdapter(mSuggestionsAdapter);
            ((SuggestionsAdapter) mSuggestionsAdapter).setQueryRefinement(
                    mQueryRefinement ? SuggestionsAdapter.REFINE_ALL
                            : SuggestionsAdapter.REFINE_BY_ENTRY);
        }
    
private voidupdateSubmitArea()

        int visibility = GONE;
        if (isSubmitAreaEnabled()
                && (mSubmitButton.getVisibility() == VISIBLE
                || mVoiceButton.getVisibility() == VISIBLE)) {
            visibility = VISIBLE;
        }
        mSubmitArea.setVisibility(visibility);
    
private voidupdateSubmitButton(boolean hasText)

        int visibility = GONE;
        if (mSubmitButtonEnabled && isSubmitAreaEnabled() && hasFocus()
                && (hasText || !mVoiceButtonEnabled)) {
            visibility = VISIBLE;
        }
        mSubmitButton.setVisibility(visibility);
    
private voidupdateViewsVisibility(boolean collapsed)

        mIconified = collapsed;
        // Visibility of views that are visible when collapsed
        final int visCollapsed = collapsed ? VISIBLE : GONE;
        // Is there text in the query
        final boolean hasText = !TextUtils.isEmpty(mQueryTextView.getText());

        mSearchButton.setVisibility(visCollapsed);
        updateSubmitButton(hasText);
        mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE);
        mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE);
        updateCloseButton();
        updateVoiceButton(!hasText);
        updateSubmitArea();
    
private voidupdateVoiceButton(boolean empty)
Update the visibility of the voice button. There are actually two voice search modes, either of which will activate the button.

param
empty whether the search query text field is empty. If it is, then the other criteria apply to make the voice button visible.

        int visibility = GONE;
        if (mVoiceButtonEnabled && !isIconified() && empty) {
            visibility = VISIBLE;
            mSubmitButton.setVisibility(GONE);
        }
        mVoiceButton.setVisibility(visibility);