独立搜索代码

package com.android.search.test;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SectionIndexer;
import android.widget.TextView;

public class SearchTestActivity extends Activity implements OnClickListener, OnItemClickListener,
android.widget.AbsListView.OnScrollListener, TextWatcher, TextView.OnEditorActionListener,
OnFocusChangeListener, OnTouchListener{
/** Called when the activity is first created. */
private String TAG="=SearchTestActivity=";
private static final int SUBACTIVITY_SEARCH = 4;
private static final int SUBACTIVITY_FILTER = 5;


static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID, // 0
"display_name", // 1
"display_name",// "display_name_alt", // 2
"display_name",// "sort_key", // 3
Contacts.STARRED, // 4
Contacts.TIMES_CONTACTED, // 5
Contacts.CONTACT_PRESENCE, // 6
Contacts.PHOTO_ID, // 7
Contacts.LOOKUP_KEY, // 8
"display_name",// "phonetic_name", // 9
Contacts.HAS_PHONE_NUMBER, // 10
};
static final String[] CONTACTS_SUMMARY_PROJECTION_FROM_EMAIL = new String[] {
Contacts._ID, // 0
"display_name", // 1
"display_name", // "display_name_alt", // 2
"display_name",// "sort_key",// "sort_key", // 3
Contacts.STARRED, // 4
Contacts.TIMES_CONTACTED, // 5
Contacts.CONTACT_PRESENCE, // 6
Contacts.PHOTO_ID, // 7
Contacts.LOOKUP_KEY, // 8
"display_name",// "phonetic_name", // 9
// email lookup doesn't included HAS_PHONE_NUMBER in projection
};

static final String[] CONTACTS_SUMMARY_FILTER_PROJECTION = new String[] {
Contacts._ID, // 0
"display_name", // 1
"display_name",// "display_name_alt", // 2
"display_name",// "sort_key",// "sort_key", // 3
Contacts.STARRED, // 4
Contacts.TIMES_CONTACTED, // 5
Contacts.CONTACT_PRESENCE, // 6
Contacts.PHOTO_ID, // 7
Contacts.LOOKUP_KEY, // 8
"display_name",// "phonetic_name", // 9
Contacts.HAS_PHONE_NUMBER, // 10
"display_name",// "snippet_mimetype", // 11
"display_name",// "snippet_data1", // 12
"display_name", // "snippet_data4", // 13
};
static final int SUMMARY_ID_COLUMN_INDEX = 0;
static final int SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX = 1;
static final int SUMMARY_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX = 2;
static final int SUMMARY_SORT_KEY_PRIMARY_COLUMN_INDEX = 3;
static final int SUMMARY_STARRED_COLUMN_INDEX = 4;
static final int SUMMARY_TIMES_CONTACTED_COLUMN_INDEX = 5;
static final int SUMMARY_PRESENCE_STATUS_COLUMN_INDEX = 6;
static final int SUMMARY_PHOTO_ID_COLUMN_INDEX = 7;
static final int SUMMARY_LOOKUP_KEY_COLUMN_INDEX = 8;
static final int SUMMARY_PHONETIC_NAME_COLUMN_INDEX = 9;
static final int SUMMARY_HAS_PHONE_COLUMN_INDEX = 10;
static final int SUMMARY_SNIPPET_MIMETYPE_COLUMN_INDEX = 11;
static final int SUMMARY_SNIPPET_DATA1_COLUMN_INDEX = 12;
static final int SUMMARY_SNIPPET_DATA4_COLUMN_INDEX = 13;

private boolean mJustCreated;
private boolean mDisplayOnlyPhones;

private boolean mSearchMode;

private boolean mSearchInitiated;

private String mInitialFilter;

private static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP+ "=1";
private static final String CLAUSE_ONLY_PHONES = Contacts.HAS_PHONE_NUMBER+ "=1";

// Uri matcher for contact id
private static final int CONTACTS_ID = 1001;
private static final UriMatcher sContactsIdMatcher;

final String[] sLookupProjection = new String[] { Contacts.LOOKUP_KEY };

static {
sContactsIdMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sContactsIdMatcher.addURI(ContactsContract.AUTHORITY, "contacts/#",
CONTACTS_ID);
}

private ListView mList;
private SearchListAdapter mSearchListAdapter;
private Button mBtn;
private EditText mEdit;
private void init(){
mBtn=(Button) findViewById(R.id.btn);
mList=(ListView) findViewById(R.id.list);
mSearchListAdapter=new SearchListAdapter(SearchTestActivity.this);
mList.setAdapter(mSearchListAdapter);
mList.setOnFocusChangeListener(this);
mList.setOnTouchListener(this);
mList.setSaveEnabled(false);
mBtn.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
onSearchRequested();
}
});
mEdit=(EditText) findViewById(R.id.search_edit);
mEdit.addTextChangedListener(this);
mEdit.setOnEditorActionListener(this);
mEdit.setText(mInitialFilter);
mEdit.requestFocus();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
}

void startQuery() {
Log.i(TAG, "=====startQuery()==");
// Set the proper empty string
setEmptyText();
mSearchListAdapter.setLoading(true);
// When sort order and display order contradict each other, we want to
// highlight the part of the name used for sorting.
String[] projection = getProjectionForQuery();
if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
mSearchListAdapter.changeCursor(new MatrixCursor(projection));
return;
}

}
private void setEmptyText() {
if ( mSearchMode) {
return;
}
}

private String getTextFilter() {
if (mEdit != null) {
return mEdit.getText().toString();
}
return null;
}
private void hideSoftKeyboard() {
// Hide soft keyboard, if visible
InputMethodManager inputMethodManager = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
}
String[] getProjectionForQuery() {
return CONTACTS_SUMMARY_FILTER_PROJECTION;
}
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
boolean globalSearch) {
Log.i(TAG, "===startSearch=globalSearch="+globalSearch);
if (globalSearch) {
super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
} else {
if (!mSearchMode) {
/*if ((mMode & MODE_MASK_PICKER) != 0) {
ContactsSearchManager.startSearchForResult(this, initialQuery,
SUBACTIVITY_FILTER);
} else {
ContactsSearchManager.startSearch(this, initialQuery);
}*/
}
}
}

/**
* Called from a background thread to do the filter and return the resulting cursor.
*
* @param filter the text that was entered to filter on
* @return a cursor with the results of the filter
*/
Cursor doFilter(String filter) {
//Cursor cursor;
String[] projection = getProjectionForQuery();
if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
return new MatrixCursor(projection);
}

final ContentResolver resolver = getContentResolver();
Log.i(TAG, "====getContactFilterUri==="+getContactFilterUri(filter)+"==filter="+filter);
return resolver.query(getContactFilterUri(filter), projection,
getContactSelection(), null,null);
}

private String getContactSelection() {
if (mDisplayOnlyPhones) {
return CLAUSE_ONLY_VISIBLE + " AND " + CLAUSE_ONLY_PHONES;
} else {
return CLAUSE_ONLY_VISIBLE;
}
}

private Uri getContactFilterUri(String filter) {
Uri baseUri;
if (!TextUtils.isEmpty(filter)) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
} else {
baseUri = Contacts.CONTENT_URI;
}

if (true) {
return buildSectionIndexerUri(baseUri);
} else {
return baseUri;
}
}
private static Uri buildSectionIndexerUri(Uri uri) {
return uri.buildUpon().appendQueryParameter("address_book_index_extras", "true").build();
//return null;
}

// TODO: fix PluralRules to handle zero correctly and use Resources.getQuantityText directly
protected String getQuantityText(int count, int zeroResourceId, int pluralResourceId) {
if (count == 0) {
return getString(zeroResourceId);
} else {
String format = getResources().getQuantityText(pluralResourceId, count).toString();
return String.format(format, count);
}
}

Cursor getItemForView(View view) {
ListView listView =mList;
int index = listView.getPositionForView(view);
if (index < 0) {
return null;
}
return (Cursor) listView.getAdapter().getItem(index);
}
@Override
protected void onRestart() {
super.onRestart();
if (TextUtils.isEmpty(getTextFilter())) {
startQuery();
} else {
// Run the filtered query on the adapter
mSearchListAdapter.onContentChanged();
}
}
@Override
protected void onDestroy() {

super.onDestroy();

}

@Override
protected void onPause() {

super.onPause();
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(TAG, "=====onActivityResult()==requestCode"+requestCode);
switch (requestCode) {
case SUBACTIVITY_FILTER:
case SUBACTIVITY_SEARCH:
// Pass through results of filter or search UI
if (resultCode == RESULT_OK) {
setResult(RESULT_OK, data);
finish();
}
break;
}
}
/**
* Event handler for the use case where the user starts typing without
* bringing up the search UI first.
*/
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (!mSearchMode&& !mSearchInitiated) {
int unicodeChar = event.getUnicodeChar();
if (unicodeChar != 0) {
mSearchInitiated = true;
startSearch(new String(new int[]{unicodeChar}, 0, 1), false, null, false);
return true;
}
}
return false;
}

/**
* Event handler for search UI.
*/
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
hideSoftKeyboard();
if (TextUtils.isEmpty(getTextFilter())) {
finish();
}
return true;
}
return false;
}
/**
* Dismisses the soft keyboard when the list takes focus.
*/
public void onFocusChange(View view, boolean hasFocus) {
if (view ==mList && hasFocus) {
hideSoftKeyboard();
}
}

/**
* Dismisses the soft keyboard when the list takes focus.
*/
public boolean onTouch(View view, MotionEvent event) {
if (view == mList) {
hideSoftKeyboard();
}
return false;
}

/**
* Dismisses the search UI along with the keyboard if the filter text is empty.
*/
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (mSearchMode && keyCode == KeyEvent.KEYCODE_BACK && TextUtils.isEmpty(getTextFilter())) {
hideSoftKeyboard();
onBackPressed();
return true;
}
return false;
}

@Override
protected void onResume() {
super.onResume();

// See if we were invoked with a filter
if (mSearchMode) {
mEdit.requestFocus();
}
if (mJustCreated) {
startQuery();
}
mJustCreated = false;
mSearchInitiated = false;
}

/**
* Performs filtering of the list based on the search query entered in the
* search text edit.
*/
protected void onSearchTextChanged() {
Log.i(TAG, "===onSearchTextChanged");
// Set the proper empty string
setEmptyText();

Filter filter = mSearchListAdapter.getFilter();
filter.filter(getTextFilter());
}
private final class SearchListAdapter extends CursorAdapter implements SectionIndexer, OnScrollListener{
private LayoutInflater mLayoutInflater;
private SectionIndexer mIndexer;
private boolean mLoading = true;
private int mFrequentSeparatorPos = ListView.INVALID_POSITION;
private Cursor mSuggestionsCursor;
private Cursor mCursor;
private int mSuggestionsCursorCount;
private Context mContext;

public SearchListAdapter(Context context) {
super(context, null,false);
Log.i(TAG, "==SearchListAdapter=");
mContext=context;
mLayoutInflater = LayoutInflater.from(context);
}

public void setSuggestionsCursor(Cursor cursor) {
if (cursor != null) {
Log.i(TAG, "=cursor=" + cursor.getCount());
}
if (mSuggestionsCursor != null) {
mSuggestionsCursor.close();
}
mSuggestionsCursor = cursor;
mSuggestionsCursorCount = cursor == null ? 0 : cursor.getCount();
}

public void setLoading(boolean loading) {
mLoading = loading;
}

@Override
public boolean isEmpty() {
Log.i(TAG, "==isEmpty=");
if (mLoading) {
return false;
} else {
return super.isEmpty();
}
}

@Override
public int getItemViewType(int position) {
Log.i(TAG, "==getItemViewType=");
if (getSeparatorId(position) != 0) {
// We don't want the separator view to be recycled.
return IGNORE_ITEM_VIEW_TYPE;
}
return super.getItemViewType(position);
}

private int getSeparatorId(int position) {
Log.i(TAG, "==getSeparatorId=");
int separatorId = 0;
return separatorId;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
Log.i(TAG, "bindView=cursror="+cursor.getCount());
TextView nameTextView = (TextView) view
.findViewById(R.id.contact_name_tv);
TextView phoTextView = (TextView) view
.findViewById(R.id.contact_phone_tv);
ImageView headImageView = (ImageView) view
.findViewById(R.id.contact_imageview);
if(cursor!=null&&cursor.getCount()>0){
long contactId=cursor.getLong(SUMMARY_ID_COLUMN_INDEX);
nameTextView.setText(contactId+"contactId");
phoTextView.setText(cursor.getString(SUMMARY_LOOKUP_KEY_COLUMN_INDEX));

}

}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.i(TAG, "==getView=");
View v;
if (convertView == null || convertView.getTag() == null) {
v = newView(mContext,mCursor, parent);
} else {
v = convertView;
}

bindView(v, mContext,mCursor);
return v;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
Log.i(TAG, "==newView=");
return mLayoutInflater.inflate(R.layout.search_item_layout, null);

}

@Override
public void changeCursor(Cursor cursor) {
if(cursor!=null){
Log.i(TAG, "==changeCursor="+cursor.getCount());
}
if (cursor != null) {
setLoading(false);
}

// Get the split between starred and frequent items, if the mode is strequent
mFrequentSeparatorPos = ListView.INVALID_POSITION;
if (cursor != null && (cursor.getCount()) > 0) {
cursor.move(-1);
for (int i = 0; cursor.moveToNext(); i++) {
int starred = cursor.getInt(SUMMARY_STARRED_COLUMN_INDEX);
if (starred == 0) {
if (i > 0) {
// Only add the separator when there are starred items present
mFrequentSeparatorPos = i;
}
break;
}
}
}

/* if (cursor != null && mSearchResultsMode) {
TextView foundContactsText = (TextView)findViewById(R.id.search_results_found);
String text = getQuantityText(cursor.getCount(), R.string.listFoundAllContactsZero,
R.plurals.listFoundAllContacts);
foundContactsText.setText(text);
}*/

super.changeCursor(cursor);
// Update the indexer for the fast scroll widget
mCursor=cursor;
updateIndexer(cursor);
//this.notifyDataSetChanged();
}
private void updateIndexer(Cursor cursor) {
Log.i(TAG, "==updateIndexer=");
if (cursor == null) {
mIndexer = null;
return;
}
}

@Override
protected void onContentChanged() {
super.onContentChanged();
Log.i(TAG, "==onContentChanged=");
CharSequence constraint = getTextFilter();
if (!TextUtils.isEmpty(constraint)) {
// Reset the filter state then start an async filter operation
Filter filter = getFilter();
filter.filter(constraint);
} else {
// Start an async query
startQuery();
}
}

@Override
public boolean areAllItemsEnabled() {
Log.i(TAG, "==areAllItemsEnabled=");
return false;
}

@Override
public boolean isEnabled(int position) {
Log.i(TAG, "==isEnabled=");

return true;
}
@Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
Cursor cursor=doFilter(constraint.toString());
Log.i(TAG,"=runQueryOnBackgroundThread==constraint="+constraint+"==curor="+cursor.getCount());
return cursor;
}

@Override
public int getCount() {
int superCount = super.getCount();
// Log.i(TAG, "===getCount"+superCount);
if (mSuggestionsCursorCount != 0) {
// When showing suggestions, we have 2 additional list items: the "Suggestions"
// and "All contacts" headers.
return mSuggestionsCursorCount + superCount + 2;
}
else if (mFrequentSeparatorPos != ListView.INVALID_POSITION) {
// When showing strequent list, we have an additional list item - the separator.
return superCount + 1;
} else {
return superCount;
}
}

private int getRealPosition(int pos) {
Log.i(TAG, "===getRealPosition");

if (mSuggestionsCursorCount != 0) {
// When showing suggestions, we have 2 additional list items: the "Suggestions"
// and "All contacts" separators.
if (pos < mSuggestionsCursorCount + 2) {
// We are in the upper partition (Suggestions). Adjusting for the "Suggestions"
// separator.
return pos - 1;
} else {
// We are in the lower partition (All contacts). Adjusting for the size
// of the upper partition plus the two separators.
return pos - mSuggestionsCursorCount - 2;
}
} else if (mFrequentSeparatorPos == ListView.INVALID_POSITION) {
// No separator, identity map
return pos;
} else if (pos <= mFrequentSeparatorPos) {
// Before or at the separator, identity map
return pos;
} else {
// After the separator, remove 1 from the pos to get the real underlying pos
return pos - 1;
}
}

@Override
public Object getItem(int pos) {
Log.i(TAG, "===getItem");
if (mSuggestionsCursorCount != 0 && pos <= mSuggestionsCursorCount) {
mSuggestionsCursor.moveToPosition(getRealPosition(pos));
return mSuggestionsCursor;
} else if (isSearchAllContactsItemPosition(pos)){
return null;
} else {
int realPosition = getRealPosition(pos);
if (realPosition < 0) {
return null;
}
return super.getItem(realPosition);
}
}

private boolean isSearchAllContactsItemPosition(int position) {
Log.i(TAG, "==isSearchAllContactsItemPosition=");
return mSearchMode && position == getCount() - 1;
}

@Override
public long getItemId(int pos) {
Log.i(TAG, "===getItemId");
if (mSuggestionsCursorCount != 0 && pos < mSuggestionsCursorCount + 2) {
if (mSuggestionsCursor.moveToPosition(pos - 1)) {
return mSuggestionsCursor.getLong(0);
} else {
return 0;
}
}
int realPosition = getRealPosition(pos);
if (realPosition < 0) {
return 0;
}
return super.getItemId(realPosition);
}

public Object [] getSections() {
Log.i(TAG, "==getSections=");
if (mIndexer == null) {
return new String[] { " " };
} else {
return mIndexer.getSections();
}
}

public int getPositionForSection(int sectionIndex) {
Log.i(TAG, "==getPositionForSection=");
if (mIndexer == null) {
return -1;
}

return mIndexer.getPositionForSection(sectionIndex);
}

public int getSectionForPosition(int position) {
Log.i(TAG, "==getSectionForPosition=");
if (mIndexer == null) {
return -1;
}

return mIndexer.getSectionForPosition(position);
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub

}

@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub

}


}
@Override
public void afterTextChanged(Editable arg0) {
onSearchTextChanged();

}

@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub

}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// TODO Auto-generated method stub

}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// TODO Auto-generated method stub

}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub

}
}

总的说来,搜索界面是一个AutoCompleteTextView,他的数据是一个SuggestionsAdapter,每次输入内容后,更新AutoCompleteTextView中的mFilter, 然后在contacts中的peopleLookup表中查找,更新adapter,然后将结果显示给用户

1,当我们点击联系人中menu->search后,调用了activity中的startSearch函数,该函数的几个参数值
得注意,因为指定了是否进行全局搜索、初始搜索的值

case MENU_SEARCH:

startSearch(null, false, null, false);

return true;

2,在SearchManager.java中实例化了一个SearchDialog的对象,然后调用show方法来创建一个搜索框(因为dialog控件是通过show来调用onCreate函数的),在onCreate函数中对输入框注册了连个监听函数

mSearchTextField.addTextChangedListener(mTextWatcher);

mSearchTextField.setOnKeyListener(mTextKeyListener);

用来在输入框的内容发生改变后,更新mUserQuery,另外更新些其他信息,例如selection的start和end

3,在SearchDialog.java中,提供了一个很重要的Adatpter,它是继承SimpleCursorAdapter的,在show方法中,可以看到如下一段代码:

mSuggestionsAdapter = new SuggestionsAdapter(getContext(), mSearchable, mSearchTextField);

mSearchTextField.setAdapter(mSuggestionsAdapter);

mSuggestionsAdapter.setNonUserQuery(false);

mSearchTextField.setText(initialQuery);

可别小看了mSearchTextField.setText(initialQuery),由于我们没有指定初始查询值,可以在这段函数前面看到initialQuery被初始成了 “”.

4,由于AutoCompleteTextView注册了一个TextWatcher,因此在输入框在setText后,调用了sendAfterTextChanged->list.get(i).afterTextChanged(text)->AutoCompleteTextView.MyWatcher.afterTextChanged->doAfterTextChanged,在doAfterTextChanged函数中,调用了performFiltering(AutoCompleteTextView.java),该函数更新了mFliter中的filter:

mFilter.filter(text, this);//在该函数中创建一个查询线程,然后将句柄为FILTER_TOKEN的消息加入到该线程的消息队列中,方便在handleMessage时得知当前的消息,这个在后面可以看到

5,查询线程执行FILTER_TOKEN消息,在Filter.java中的handleMessage函数执行如下分支:

case FILTER_TOKEN:

..........

//执行了搜索

args.results = performFiltering(args.constraint);

............

............

//添加新的消息到消息队列

Message finishMessage = mThreadHandler.obtainMessage(FINISH_TOKEN);

mThreadHandler.sendMessageDelayed(finishMessage, 3000);

}

break;

6,在filter.java中再来看看performFiltering的实现

Cursor cursor = mClient.runQueryOnBackgroundThread(constraint);

由于此时的mClient就是SuggestionAdapter,所以runQueryOnBackgroundThread(constraint);在SuggestionAdapter中实现的,关键代码如下:

c = getSuggestions(mSearchable, query);

getSuggestions函数中可以看到它构建了uri,然后调用了ContentResolver中的query函数

进一步跟踪到AbstractSyncableContentProvider中query函数,发现它调用了queryInternal(url, projection, selection, selectionArgs, sortOrder);(在ContactsProvider中实现的)

7,查询完成后,ResultsHandler.handleMessage负责更新将CursorAdapter.java中的mCursor,同时调用notifyDataSetChanged();改变Adapter; 然后调用onFilterComplete更新数据显示

publishResults(args.constraint, args.results);

args.listener.onFilterComplete(count);//由AutoCompleteTextView.onFilterComplete更新数据

8,如果搜索不到,就会在CursorAdapter.changeCursor中调用notifyDataSetInvalidated();通知;

9,当在搜索框中输入了字符,调用AutoCompleteTextView中的doAfterChanged函数,从而执行上面4到8的步骤

备注:联系人中查询的uri是”content://contacts/search_suggest_query/输入的字符“,首先在peopleLookup表中查找到联系人的ID,然后到people, phones, contact_methods, organizations表中查找对应联系人的信息


更多相关文章

  1. Android:UI更新方法二:View.postInvalidate+Thread+Runnable
  2. Android(安卓)Uri.getQueryParameter使用注意
  3. 调用getChildFragmentManager时出现的Bug
  4. Android(安卓)socket通信app Client端
  5. Android(安卓)直接通过JNI访问驱动
  6. android 7.0 user版本调试方法
  7. Android(安卓)SQLite数据库增删改查
  8. android支付宝 KeyFactory PrivateKey
  9. TypedefViewTest

随机推荐

  1. C++与java混合编写和C++与OC混合编写 笔
  2. Android开启闪光灯的几种办法
  3. 【30篇突击 android】源码统计 十七
  4. Android(安卓)SQLite 建立多表间的主外键
  5. android细节:各种context
  6. android 待机流程
  7. eclipse插件下载--全
  8. 解析Android(安卓)mdpi hdpi xhdpi xxhdp
  9. 安卓开发:TabHost 选项卡的基本用法
  10. android AudioTrack 播放 正弦波 方波