美文网首页
联系人详情Recent列表浅析

联系人详情Recent列表浅析

作者: sp_zijing | 来源:发表于2017-11-15 15:19 被阅读0次
    需求:修改联系人详情界面中的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

    相关文章

      网友评论

          本文标题:联系人详情Recent列表浅析

          本文链接:https://www.haomeiwen.com/subject/hczcvxtx.html