SearchView 源码解析
SearchViewandroid.support.v7.widget
SearchViewAPI >= 7,android.widget.SearchView
API >= 11
android.support.v4.widget.SearchViewCompat
v723.2.1
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;
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)
SearchView
layoutlayoutid
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;
}
SearchView
Listener
//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
//
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());
}
//
}
//
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
SearchViewCollapsibleActionViewonActionViewExpanded()onActionViewCollapsed(),
mExpandedInActionView
ActionViewSearchViewMenuItem
v7menuSearchViewMenuItemCompatdemo
MenuItemCompat.getActionView(android.view.MenuItem item);
SearchViewonSaveInstanceState()onRestoreInstanceState(Parcelable state)
boolean mIconified
SavedStatemIconified
//Parcelable
static class SavedState extends BaseSavedState {
boolean isIconified;
/**
*
*/
}
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
SearchView(API >= 8)Searchablexml
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
showVoiceSearchButton``launchRecognizer
searchable 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
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);
}
}
onMeasure
isIconified()``false
widthmodeMeasureSpec.EXACTLY
SearchViewonMeasure
widthmode, 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);
}
v7SearchView:
private final AppCompatDrawableManager mDrawableManager;
//
mDrawableManager = AppCompatDrawableManager.get();
! AppCompatDrawableManager
Drawable
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
()
ObjectAnimator``AnimatorSet
VectorDrawable: Tint``Cache
:)