SearchViewAnalysis

SearchView 源码解析

Stars
155

SearchView

SearchViewandroid.support.v7.widgetSearchViewAPI >= 7,android.widget.SearchViewAPI >= 11 android.support.v4.widget.SearchViewCompat

  • 1.
    • 1.1
    • 1.2
    • 1.3
    • 1.4 Listener
    • 1.5 CollapsibleActionView
    • 1.6
    • 1.7 SuggestionsSearchable
    • 1.8
    • 1.9 AutoCompleteTextViewReflector
    • 1.10 onMeasure
  • 2.

1.

v723.2.1

1.1

1.2


    private final SearchAutoComplete mSearchSrcTextView;
    private final View mSearchEditFrame;
    private final View mSearchPlate;
    private final View mSubmitArea;
    private final ImageView mSearchButton;
    private final ImageView mGoButton;
    private final ImageView mCloseButton;
    private final ImageView mVoiceButton;
    private final View mDropDownAnchor;
    private final ImageView mCollapsedIcon;

1.3

public SearchView(Context context, AttributeSet attrs, int defStyleAttr),v7``SearchView``TypedArray``TintTypedArray``TintTypedArray```` private final TypedArray mWrapped; ````TypedArray``getDrawable(int index)``getDrawableIfKnown(int index) AppCompatDrawableManager.get().getDrawable(mContext, resourceId)

SearchViewlayoutlayoutid findViewById(id)

mIconifiedByDefault``true``setIconifiedByDefault(boolean iconified)

    public void setIconifiedByDefault(boolean iconified) {
        if (mIconifiedByDefault == iconified) return;
        mIconifiedByDefault = iconified;
        //
        updateViewsVisibility(iconified);
        updateQueryHint();
    }

setIconifiedByDefault(false)SearchViewiconupdateQueryHint()


    private CharSequence getDecoratedHint(CharSequence hintText) {
        //mIconifiedByDefaultfalsemSearchHintIconnull
        //iconhint
        if (!mIconifiedByDefault || mSearchHintIcon == null) {
            return hintText;
        }

        final int textSize = (int) (mSearchSrcTextView.getTextSize() * 1.25);
        mSearchHintIcon.setBounds(0, 0, textSize, textSize);

        final SpannableStringBuilder ssb = new SpannableStringBuilder("   ");
        ssb.setSpan(new ImageSpan(mSearchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        ssb.append(hintText);
        return ssb;
    }

1.4 Listener

SearchViewListener


    //2
        //onQueryTextSubmit(String query)
        //onQueryTextChange(String newText)
    private OnQueryTextListener mOnQueryChangeListener;
    
    //1boolean onClose();
        //onClose()mCloseButtonsetIconified(true)
        //onCloseClicked() 
    private OnCloseListener mOnCloseListener;
    
    //View
    private OnFocusChangeListener mOnQueryTextFocusChangeListener;
    
    //2
        //onSuggestionSelect(int position)
        //onSuggestionClick(int position)
    private OnSuggestionListener mOnSuggestionListener;
    
    //View
    private OnClickListener mOnSearchClickListener;

    //mOnClickListenermTextKeyListener

OnQueryTextListener

  • onQueryTextChange(String newText)
    //
    mSearchSrcTextView.addTextChangedListener(mTextWatcher);

mTextWatcher``onTextChanged()SearchViewonTextChanged(CharSequence newText) :


    private void onTextChanged(CharSequence newText) {
        /**
         * ,
         */

        //listener!=null
        if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)) {
            mOnQueryChangeListener.onQueryTextChange(newText.toString());
        }

        //
    }

  • onQueryTextSubmit(String query)

    //
    mSearchSrcTextView.setOnEditorActionListener(mOnEditorActionListener);

    private final OnEditorActionListener mOnEditorActionListener = new OnEditorActionListener() {
    
        /**
         * Called when the input method default action key is pressed.
         */
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            onSubmitQuery();
            return true;
        }
    };

    private void onSubmitQuery() {
        CharSequence query = mSearchSrcTextView.getText();
        if (query != null && TextUtils.getTrimmedLength(query) > 0) {
            
            //OnQueryChangeListener
            //onQueryTextSubmit() return true
            if (mOnQueryChangeListener == null
                    || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
                    
                //SearchablestartActivityActivity    
                if (mSearchable != null) {
                    launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
                }
                //
                setImeVisibility(false); 
                
                //ListPopupWindow AutoCompleteTextView 
                //dismiss
                dismissSuggestions();
            }
        }
    }

if!mOnQueryChangeListener.onQueryTextSubmit(query.toString()),

Listener

1.5 CollapsibleActionView

SearchViewCollapsibleActionViewonActionViewExpanded()onActionViewCollapsed(), mExpandedInActionViewActionViewSearchViewMenuItem v7menuSearchViewMenuItemCompatdemo

    MenuItemCompat.getActionView(android.view.MenuItem item);

1.6

SearchViewonSaveInstanceState()onRestoreInstanceState(Parcelable state) boolean mIconifiedSavedStatemIconified


    //Parcelable
    static class SavedState extends BaseSavedState {
        boolean isIconified;

        /**
         * 
         */
    }

1.7 SuggestionsSearchable

SuggestionssetSearchableInfolog

W/SearchView: Search suggestions cursor at row 0 returned exception.
              java.lang.NullPointerException
                  at android.support.v7.widget.SearchView.createIntentFromSuggestion(SearchView.java:1620)
                  at android.support.v7.widget.SearchView.launchSuggestion(SearchView.java:1436)
                  at android.support.v7.widget.SearchView.onItemClicked(SearchView.java:1349)
                  at android.support.v7.widget.SearchView.access$1800(SearchView.java:103)
                  at android.support.v7.widget.SearchView$10.onItemClick(SearchView.java:1373)
                  ......

1620

    private Intent createIntentFromSuggestion(Cursor c, int actionKey, String actionMsg) {
        try {

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

            //mSearchablenull
            if (action == null && Build.VERSION.SDK_INT >= 8) {
                action = mSearchable.getSuggestIntentAction();  //1620
            }

            /**
             *
             */

            return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
        } catch (RuntimeException e ) {

            /**
             *
             */

            Log.w(LOG_TAG, "Search suggestions cursor at row " + rowNum +
                                    " returned exception.", e);
            return null;
        }
    }

mSearchablemSearchablenullcatchcrash setOnSuggestionListener

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

        @Override
        public boolean onQueryTextChange(String newText) {
            return true; //true
        }
    });

onSuggestionClick(int position) true createIntentFromSuggestion(~) logpopdismissSearchViewclearFocus()

nullSearchablestartActivity(createIntentFromSuggestion(~)) startActivity

1.8

SearchView(API >= 8)Searchablexml

android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"

showVoiceSearchButton``launchRecognizersearchable activity boolean mVoiceButtonEnabled``setSearchableInfo(~)

mVoiceButtonEnabled = IS_AT_LEAST_FROYO && hasVoiceSearch();

IS_AT_LEAST_FROYOBuild.VERSION.SDK_INT >= 8 debughasVoiceSearch()

    ResolveInfo ri = getContext().getPackageManager().resolveActivity(testIntent,
            PackageManager.MATCH_DEFAULT_ONLY);
    return ri != null;

resolveActivityreturn falsemVoiceButtonEnabledfalse(><)

//Genymotion(), AS(Google)resolveGoogleActivityRecognizerIntent

1.9 AutoCompleteTextViewReflector

v7SearchViewAutoCompleteTextViewInputMethodManager


    static final AutoCompleteTextViewReflector HIDDEN_METHOD_INVOKER = new AutoCompleteTextViewReflector();

    private static class AutoCompleteTextViewReflector {
        private Method doBeforeTextChanged, doAfterTextChanged;
        private Method ensureImeVisible;
        private Method showSoftInputUnchecked;

        AutoCompleteTextViewReflector() {
            
            /**
             * 
             */
            
            try {
                showSoftInputUnchecked = InputMethodManager.class.getMethod(
                        "showSoftInputUnchecked", int.class, ResultReceiver.class);
                showSoftInputUnchecked.setAccessible(true);
            } catch (NoSuchMethodException e) {
                // Ah well.
            }
        }    
    
        /**
         * 
         */        
    
        void showSoftInputUnchecked(InputMethodManager imm, View view, int flags) {
            if (showSoftInputUnchecked != null) {
                try {
                    showSoftInputUnchecked.invoke(imm, flags, null);
                    return;
                } catch (Exception e) {
                }
            }

            //if
            // Hidden method failed, call public version instead
            imm.showSoftInput(view, flags);
        }        
    }

1.10 onMeasure

onMeasure isIconified()``falsewidthmodeMeasureSpec.EXACTLY SearchViewonMeasurewidthmode, modeEXACTLYsuper

EXACTLYviewwidthheight?

layout layout_heightmatch_parent(EXACTLY)layout_widthwrap_content(AT_MOST) onMeasure, //...


    @Override
    protected void onMeasure(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);
    }

2.

v7SearchView:

    private final AppCompatDrawableManager mDrawableManager;

    //
    mDrawableManager = AppCompatDrawableManager.get();

! AppCompatDrawableManagerDrawable AppCompatDrawableManager

get():


    public static AppCompatDrawableManager get() {
        //
        if (INSTANCE == null) {
            INSTANCE = new AppCompatDrawableManager();
            installDefaultInflateDelegates(INSTANCE);
        }
        return INSTANCE;
    }


    private static void installDefaultInflateDelegates(@NonNull AppCompatDrawableManager manager) {
        final int sdk = Build.VERSION.SDK_INT;
        // Android 5.0
        if (sdk < 21) {
            //  VectorDrawableCompat 
            manager.addDelegate("vector", new VdcInflateDelegate());

            if (sdk >= 11) {
                // AnimatedVectorDrawableCompat  API v11+ 
                manager.addDelegate("animated-vector", new AvdcInflateDelegate());
            }
        }
    }

, Vector()

: Tint``Cache


:)

Related Projects