联系人详情Recent列表浅析

2017-11-15  本文已影响0人  sp_zijing
需求:修改联系人详情界面中的CallLog记录,使其能够指示该呼叫产生于哪一张SIM卡。
原生效果.png

在Android M中,联系人详情统一到了QuickContactActivity.java这个类中。详情界面涉及到联系人的信息,Recent列表还包含CallLog,SMS,Calendar事件信息。

CallLog,SMS,Calendar事件的加载显示流程大同小异,因为本文需求是修改CallLog的图标,因此以CallLog为例。

这些数据肯定是要在Activity启动时加载的,我们在onResume中找到了startInteractionLoaders这么个方法,部分实现如下:

getLoaderManager().initLoader( LOADER_CALL_LOG_ID, phonesExtraBundle, mLoaderInteractionsCallbacks);

启动了一个Loader,ID为LOADER_CALL_LOG_ID,回调MLoaderInteractionsCallbacks,没啥说的,去回调中看看再说:

loader = new CallLogInteractionsLoader(
                                    QuickContactActivity.this,
                                    args.getStringArray(KEY_LOADER_EXTRA_PHONES),
                                    MAX_CALL_LOG_RETRIEVE);

好了,这里找到了Loader的具体实现类。CallLogInteractionsLoader的后两个参数分别是电话号码和查询的最大条数。我们很关心这个Loader到底会加载哪些数据,因为要实现需求,我们需要知道呼叫记录产生的SIM卡。看看它的loadInBackground的实现:

for (String number : mPhoneNumbers) {
            interactions.addAll(getCallLogInteractions(number));
}

继续查看getCallLogInteractions实现:

final Cursor cursor = getContext().getContentResolver().query(uri, null, null, null,
                orderByAndLimit);

query的参数都是null。Good!要啥有啥!

我们回过头来,继续看Loader的onLoadFinished回调:

mRecentLoaderResults.put(loader.getId(), data);
if (isAllRecentDataLoaded()) {
       bindRecentData();
}

嗯,把数据存储在mRecentLoaderResults中,然后bindRecentData,准备绑定view啦!

mRecentDataTask = new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                                     …………
                // Wrap each interaction in its own list so that an icon is displayed for each entry
                for (Entry contactInteraction : contactInteractionsToEntries(allInteractions)) {
                    List<Entry> entryListWrapper = new ArrayList<>(1);
                    entryListWrapper.add(contactInteraction);
                    interactionsWrapper.add(entryListWrapper);
                }

                Trace.endSection();
                return null;
            }

            @Override
            protected void onPostExecute(Void aVoid) {
                super.onPostExecute(aVoid);
                Trace.beginSection("initialize recents card");

                if (allInteractions.size() > 0) {
                    mRecentCard.initialize(interactionsWrapper,
                    /* numInitialVisibleEntries = */ MIN_NUM_COLLAPSED_RECENT_ENTRIES_SHOWN,
                    /* isExpanded = */ mRecentCard.isExpanded(), /* isAlwaysExpanded = */ false,
                            mExpandingEntryCardViewListener, mScroller);
                    mRecentCard.setVisibility(View.VISIBLE);
                }
                                    …………
            }
        };

在bindRecentData中是用Asynctask先对查找结果按日期排序了,将排序结果从contactInteractions转换成了Entry,存入interactionWrapper中,然后在onPostExecute中来初始化mRecentCard。初始话的工作无非就是各种赋初值,然后设置监听啊等。在上面新建Loader实力的时候,是传入了一个context的,使用这个context创建了一个LayoutInflater,这里才是view真正产生的地方,代码如下:

entryViewList.add(createEntryView(layoutInflater, entryList.get(0), /* showIcon = */ View.VISIBLE));

createEntryView这个方法比较长,都是view里的内容的设置,包括icon,header等等。这里猜测我们要改的就是Icon了。为了验证,我们用HierarchyViewer来查看一下:


ExpandingEntryCardView.png

嗯,没毛病,看来修改icon的资源文件就行了。来看看开始是怎么样的:

        final ImageView icon = (ImageView) view.findViewById(R.id.icon);
        icon.setVisibility(iconVisibility);
        if (entry.getIcon() != null) {
            icon.setImageDrawable(entry.getIcon());
        }

是从entry获取的。这个Entry是ExpandingEntryCardView的一个内部类。这里我们又要回头去看看这个entry是何时用了啥资源创建出来的了。前文在bindRecentData的时候有提到过从contactInteractions转为Entry,没有细看,这里我们要看看他的实现了:

private List<Entry> contactInteractionsToEntries(List<ContactInteraction> interactions) {
        final List<Entry> entries = new ArrayList<>();
        for (ContactInteraction interaction : interactions) {
            if (interaction == null) {
                continue;
            }
            entries.add(new Entry(/* id = */ -1,
                    interaction.getIcon(this),
                    interaction.getViewHeader(this),
                    interaction.getViewBody(this),
                    interaction.getBodyIcon(this),
                    interaction.getViewFooter(this),
                    interaction.getFooterIcon(this),
                    interaction.getContentDescription(this),
                    interaction.getIntent(),
                    /* alternateIcon = */ null,
                    /* alternateIntent = */ null,
                    /* alternateContentDescription = */ null,
                    /* shouldApplyColor = */ true,
                    /* isEditable = */ false,
                    /* EntryContextMenuInfo = */ null,
                    /* thirdIcon = */ null,
                    /* thirdIntent = */ null,
                    /* thirdContentDescription = */ null,
                    /* thirdAction = */ Entry.ACTION_NONE,
                    /* thirdActionExtras = */ null,
                    interaction.getIconResourceId()));
        }
        return entries;
    }

发现就是重新把数据换个类存放而已。这里Interaction是接口,CallLog,SMS,Calendar有各自的实现,CallLogInteraction的getIcon实现如下:
return context.getResources().getDrawable(CALL_LOG_ICON_RES);
如此直白。依据需求我们根据SIM卡号设置对应资源文件就行了。

        if (mValues.getAsInteger(Calls.SLOT_ID) == 0)
            return context.getResources().getDrawable(CALL_LOG_ICON_RES_SIM1);
        else
            return context.getResources().getDrawable(CALL_LOG_ICON_RES_SIM2);
修改后效果.png
上一篇下一篇

猜你喜欢

热点阅读