android 联系人搜索

独立搜索代码

 

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表中查找对应联系人的信息


你可能感兴趣的:(android 联系人搜索)