拨号盘搜索联系人

2,拨号盘搜索联系人

拨号盘搜索联系人流程图如下,


拨号界面对应的fragment为DialpadFragment,并且DialpadFragment实现了TextWatcher接口,

afterTextChanged方法如下,

if (mDialpadQueryListener != null) {
   mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());
}

调用mDialpadQueryListener监听器mDialpadQueryListener的onDialpadQueryChanged方法,

DialtactsActivity的onDialpadQueryChanged方法主要逻辑如下,

1,首先调用SmartDialNameMatcher的normalizeNumber方法进行字符转化,其实还是数字,

final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
                SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);

2,然后将匹配后的字符为mSearchView控件赋值,

if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
•••
mSearchView.setText(normalizedQuery);

在DialtactsActivity的onCreate方法中,为mSearchView添加了监听器,

mSearchView = (EditText) searchEditTextLayout.findViewById(R.id.search_view);
mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);

mPhoneSearchQueryTextListener是TextWatcher接口的匿名实现类。

当mSearchView的值发生变化时,就会调用mPhoneSearchQueryTextListener的onTextChanged方法,onTextChanged方法主要逻辑如下,

1,如果查询字符和上次的值相同,就直接返回,

if (newText.equals(mSearchQuery)) {
    return;
}

2,将查询字符保存在mSearchQuery中,

mSearchQuery = newText;

3,如果当前是SmartDialSearchFragment界面,就调用其setQueryString方法开始查询;

否则调用RegularSearchFragment的setQueryString方法开始查询。

if (mSmartDialSearchFragment != null && mSmartDialSearchFragment.isVisible()) {
     mSmartDialSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
} else if (mRegularSearchFragment != null && mRegularSearchFragment.isVisible()) {
     mRegularSearchFragment.setQueryString(mSearchQuery, false /* delaySelection */);
}

实际只有父类ContactEntryListFragment实现了setQueryString方法,逻辑如下,

1,保存查询字符,并将模式设置为查询模式,

mQueryString = queryString;
setSearchMode(!TextUtils.isEmpty(mQueryString) || mShowEmptyListForEmptyQuery);

2,重新加载查询联系人数据库,

mAdapter.setQueryString(queryString);
reloadData();

reloadData方法会做一些设置,并且调用startLoading方法,最后会调用LoaderManager的initLoader方法就开始查询。

1.1 初始化

SmartDialSearchFragment的createListAdapter方法如下,

SmartDialNumberListAdapter adapter = new SmartDialNumberListAdapter(getActivity());
adapter.setUseCallableUri(super.usesCallableUri());
adapter.setQuickContactEnabled(true);
// Set adapter's query string to restore previous instance state.
adapter.setQueryString(getQueryString());
return adapter;

因此,对应的adapter 为SmartDialNumberListAdapter。

onCreateLoader方法如下,

final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter();
SmartDialCursorLoader loader = new SmartDialCursorLoader(super.getContext());
adapter.configureLoader(loader);
return loader;

首先构造SmartDialCursorLoader对象,然后调用SmartDialNumberListAdapter的configureLoader方法进行设置。

对应的loader为SmartDialCursorLoader, SmartDialCursorLoader的configureLoader方法调用流程图如下,


configureLoader方法如下,

if (getQueryString() == null) { // 查询关键字为空
    loader.configureQuery("");
     mNameMatcher.setQuery("");
} else {
     loader.configureQuery(getQueryString());
     mNameMatcher.setQuery(PhoneNumberUtils.normalizeNumber(getQueryString()));
}

调用SmartDialCursorLoader的configureQuery方法进行转换。

mNameMatcher 是SmartDialNameMatcher变量,是在SmartDialNumberListAdapter方法中构造的。

SmartDialCursorLoader的configureQuery方法逻辑如下,

首先调用SmartDialNameMatcher的normalizeNumber进行转化,

然后利用mQuery构造SmartDialNameMatcher对象。

mQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialPrefix.getMap());
/** Constructs a name matcher object for matching names. */
mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap());

并且分别保存在mQuery 和 mNameMatcher变量中。

1.2 查询过程

查询过程主要看SmartDialCursorLoader,继承于AsyncTaskLoader,并且实现了loadInBackground/onCanceled/deliverResult等方法.其中最主要的是loadInBackground方法,这个方法当然是在子线程中执行的。调用流程图如下,


loadInBackground方法的主要逻辑如下,

1,构造DialerDatabaseHelper对象,然后调用其getLooseMatches方法查询结果,

final DialerDatabaseHelper dialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(
    mContext);
final ArrayList<ContactNumber> allMatches = dialerDatabaseHelper.getLooseMatches(mQuery,
     mNameMatcher);

2,将查询结果封装在MatrixCursor中, MatrixCursor其实也是继承于Cursor,

final MatrixCursor cursor = new MatrixCursor(PhoneQuery.PROJECTION_PRIMARY);
Object[] row = new Object[PhoneQuery.PROJECTION_PRIMARY.length];
for (ContactNumber contact : allMatches) {
     row[PhoneQuery.PHONE_ID] = contact.dataId;
     row[PhoneQuery.PHONE_NUMBER] = contact.phoneNumber;
     •••
     cursor.addRow(row);
}
return cursor;

DialerDatabaseHelper的getLooseMatches方法逻辑如下,

1,对dialer.db 数据库的smartdial_table和prefix_table进行查询,

final Cursor cursor = db.rawQuery("SELECT " +
    SmartDialDbColumns.DATA_ID + ", " +
    SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
    SmartDialDbColumns.PHOTO_ID + ", " +
    SmartDialDbColumns.NUMBER + ", " +
    SmartDialDbColumns.CONTACT_ID + ", " +
    SmartDialDbColumns.LOOKUP_KEY +
    " FROM " + Tables.SMARTDIAL_TABLE + " WHERE " +
    SmartDialDbColumns.CONTACT_ID + " IN " +
     " (SELECT " + PrefixColumns.CONTACT_ID +
     " FROM " + Tables.PREFIX_TABLE +
     " WHERE " + Tables.PREFIX_TABLE + "." + PrefixColumns.PREFIX +
      " LIKE '" + looseQuery + "')" +
      " ORDER BY " + SmartDialSortingOrder.SORT_ORDER,
      new String[] {currentTimeStamp});

此次查询分为2个步骤,首先获取prefix_table表中prefix字段和数字匹配的对应的contact_id字段,然后从smartdial_table表中获取对应contact_id字段的Cursor对象。

例如,在拨号盘按下1或者2,都可以搜到联系人为abc的相关信息, prefix_table表 和 smartdial_table表 是通过contact_id唯一对应起来的。如下,


2,利用查询的Cursor对象逐个获取联系人信息,

while ((cursor.moveToNext()) && (counter < MAX_ENTRIES)) {
final long dataID = cursor.getLong(columnDataId);
final String displayName = cursor.getString(columnDisplayNamePrimary);
•••

MAX_ENTRIES 为20,定义如下,

private static final int MAX_ENTRIES = 20;

也就是说,最多搜多出20条匹配结果。

3,构造ContactMatch对象,封装匹配的联系人信息,

final ContactMatch contactMatch = new ContactMatch(lookupKey, id);
if (duplicates.contains(contactMatch)) {
continue;
}

对于联系人来说, lookupKey是唯一的,但是一个联系人可以有多个号码,并且多个号码都可以匹配。如果去掉上面的代码,联系人的每个匹配信息都可以当做单独的联系人显示,否则的话,只显示第一个匹配的号码。

4,首先检查姓名和号码匹配的联系人,然后封装为ContactNumber对象,最后保存在result中, result是ArrayList类型,也是最后返回的结果,

final boolean nameMatches = nameMatcher.matches(displayName);
final boolean numberMatches =
     (nameMatcher.matchesNumber(phoneNumber, query) != null);
if (nameMatches || numberMatches) {
    /** If a contact has not been added, add it to the result and the hash set.*/
   duplicates.add(contactMatch);
   result.add(new ContactNumber(id, dataID, displayName, phoneNumber, lookupKey,
 photoId));
   counter++; // 记录匹配的联系人数目
•••

关键是SmartDialNameMatcher的matches 和matchesNumber方法,这2个方法还可以仔细分析。

RegularSearchFragment查询的是contacts2.db数据库,并且通过PhoneNumberListAdapterconfigureLoader方法设置Uri等查询信息。onCreateLoader是调用父类ContactEntryListFragment的方法创建的匿名CursorLoader进行查询的,并且数据库也是调用系统的标准数据库。

这个和SmartDialNumberListAdapter完全不同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值