Android 4.0.3 聯絡人(通訊錄)應用原始碼學習
Contacts應用入口類有2個:PeopleActivity.java和DialtactsActivity.java。PeopleActivity是聯絡人入口類,DialtactsActivity是撥號入口類,Contacts集成了聯絡人和撥號功能。Contacts主介面如圖1所示:
圖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,他是多繼承類,其結構如下圖所示:
從圖看出,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中即可。