天天看點

Android 4.0.3 聯系人(通訊錄)應用源碼學習

    Contacts應用入口類有2個:PeopleActivity.java和DialtactsActivity.java。PeopleActivity是聯系人入口類,DialtactsActivity是撥号入口類,Contacts內建了聯系人和撥号功能。Contacts主界面如圖1所示:

Android 4.0.3 聯系人(通訊錄)應用源碼學習

圖1

     Contacts有三個标簽頁組成,最左邊的是群組,中間的是所有聯系人,右邊是常用、收藏聯系人。三個标簽是ActionBar.Tab類對象,通過ActionBar的newTab()方法建構,主要代碼在ActionBarAdapter.java中,添加三個标簽代碼是:

addTab(TabState.GROUPS, R.drawable.ic_tab_groups, R.string.contactsGroupsLabel);
    addTab(TabState.ALL, R.drawable.ic_tab_all, R.string.contactsAllLabel);
    addTab(TabState.FAVORITES, R.drawable.ic_tab_starred, R.string.contactsFavoritesLabel);           

 addTab是一個自定義方法,代碼如下:

private void addTab(TabState tabState, int icon, int description) {
        final Tab tab = mActionBar.newTab();
        tab.setTag(tabState);
        tab.setTabListener(mTabListener);
        if (mShowTabsAsText) {
            tab.setText(description);
        } else {
            tab.setIcon(icon);
            tab.setContentDescription(description);
        }
        mActionBar.addTab(tab);
    }           

第二個參數icon就是标簽上的圖示。

 三個标簽能夠切換,必須實作ActionBar.TabListener監聽器接口,必須實作onTabSelected方法:

public abstract void onTabSelected (ActionBar.Tab tab, FragmentTransaction ft)           

當選擇某個标簽時講調用它。

每個标簽都和一個Fragment對應,當選擇某個标簽時,對應的Fragment就會被調用顯示。三個Fragment是:

GroupBrowseListFragment.java(群組)
     DefaultContactBrowseListFragment.java(所有聯系人)
     ContactTileListFragment.java(常用、收藏聯系人)。           

隻要是Fragment,都有一個布局與其對應:

group_browse_list_fragment.xml
     contact_list_content.xml
     contact_tile_list.xml           

在入口類PeopleActivity.java中,采用getFragmentManager()方法獲得FragmentManager執行個體便于管理Fragment,比如将Fragment從背景堆棧中彈出等。FragmentTransaction可以用來隐藏、添加、移除Fragment等操作,使用FragmentManager的beginTransaction方法,代碼: 

FragmentManager fragmentManager = getFragmentManager(); 
    FragmentTransaction transaction = fragmentManager.beginTransaction()
           

再用add方法将fragment生成的view添加到容器中,該容器嵌入在activity的布局中:

transaction.add(R.id.tab_pager, mFavoritesFragment, FAVORITE_TAG);
    transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG);
    transaction.add(R.id.tab_pager, mGroupsFragment, GROUPS_TAG);           

要使得add、删除等方法生效,必須使用commit方法送出這些事務:

transaction.commitAllowingStateLoss();           

commitAllowingStateLoss和commit的差別是當退出activity時,防止送出後的狀态丢失。

 mAllFragment是DefaultContactBrowseListFragment的執行個體,是“所有聯系人”的fragment,他是多繼承類,其結構如下圖所示:

Android 4.0.3 聯系人(通訊錄)應用源碼學習

從圖看出,DefaultContactBrowseListFragment最終繼承于Fragment,ContactListAdapter是與listview對應的自定義擴充卡,最終繼承于CompositeCursorAdapter,而CompositeCursorAdapter實際上繼承于BaseAdapter,系統采用了CompositeCursorAdapter方法的好處在于它已經實作了getView方法,并且采用了緩存技術,代碼如下:

public View getView(int position, View convertView, ViewGroup parent) {
        ensureCacheValid();
        int start = 0;
        for (int i = 0; i < mSize; i++) {
            int end = start + mPartitions[i].count;
            if (position >= start && position < end) {
                int offset = position - start;
                if (mPartitions[i].hasHeader) {
                    offset--;
                }
                View view;
                if (offset == -1) {
                    view = getHeaderView(i, mPartitions[i].cursor, convertView, parent);
                } else {
                    if (!mPartitions[i].cursor.moveToPosition(offset)) {
                        throw new IllegalStateException("Couldn't move cursor to position "
                                + offset);
                    }
                    view = getView(i, mPartitions[i].cursor, offset, convertView, parent);
                }
                if (view == null) {
                    throw new NullPointerException("View should not be null, partition: " + i
                            + " position: " + offset);
                }
                return view;
            }
            start = end;
        }

        throw new ArrayIndexOutOfBoundsException(position);
    }           

它包含一個getView,具體實作是:  

protected View getView(int partition, Cursor cursor, int position, View convertView,
            ViewGroup parent) {
        View view;
        if (convertView != null) {
            view = convertView;
        } else {
            view = newView(mContext, partition, cursor, position, parent);
        }
        bindView(view, partition, cursor, position);
        return view;
    }           

它判斷convertView是否為空,如果是,就用newView方法建立一個,如果不是,就采用已有的,這種方法無需每次都建立View對象,提高了效率。需要我們實作的隻有2個:

bindView方法和newView方法。newView方法在ContactListAdapter中實作,bindView在DefaultContactListAdapter中實作。

newView是用來建立ListView的item布局的:

protected View newView(Context context, int partition, Cursor cursor, int position,
            ViewGroup parent) {
    	
        ContactListItemView view = new ContactListItemView(context, null);
        view.setUnknownNameText(mUnknownNameText);
        view.setQuickContactEnabled(isQuickContactEnabled());
        view.setActivatedStateSupported(isSelectionVisible());
        return view;
    }           

ContactListItemView繼承ViewGroup,是每個item的布局,在這個布局中添加TextView顯示聯系人姓名,添加ImageView顯示頭像,再使用一個TextView顯示姓名大寫首字母。在ContactListItemView中,主要的實作方法有三個:

onMeasure, onLayout, dispatchDraw. onMeasure用來測量視圖的大小尺寸,onLayout給視圖布局,确定其位置,dispatchDraw起到分發的作用,正在畫圖的方法還是draw。

 如果說newView是用來建立每個Item的布局,那麼bindView是用來建立布局上每個子視圖。bindVIew的代碼如下:

protected void bindView(View itemView, int partition, Cursor cursor, int position) {
        final ContactListItemView view = (ContactListItemView)itemView;

        view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null);

        if (isSelectionVisible()) {
            view.setActivated(isSelectedContact(partition, cursor));
        }

        bindSectionHeaderAndDivider(view, position, cursor);

        if (isQuickContactEnabled()) {
            bindQuickContact(view, partition, cursor, ContactQuery.CONTACT_PHOTO_ID,
                    ContactQuery.CONTACT_ID, ContactQuery.CONTACT_LOOKUP_KEY);
        } else {
            bindPhoto(view, partition, cursor);
        }

        bindName(view, cursor);
        bindPresenceAndStatusMessage(view, cursor);

        if (isSearchMode()) {
            bindSearchSnippet(view, cursor);
        } else {
            view.setSnippet(null);
        }
    }           

其中:bindSectionHeaderAndDivider是建立第一行首字母和首字母下面藍色下劃線以及首字母右邊的計數器;bindName建立姓名TextView;

ContactEntryListFragment中實作了Fragment的onCreateView方法,該方法建立Fragment對應的視圖,建立代碼是:

mView = inflateView(inflater, container);           

inflateView的實作代碼:

protected View inflateView(LayoutInflater inflater, ViewGroup container) {
        return inflater.inflate(R.layout.contact_list_content, null);
    }           

布局檔案contact_list_content.xml即是所有聯系人的根布局。建立完視圖後,采用setAdapter将擴充卡添加到ListView中即可。