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中即可。
更多相关文章
- Andriod应用开发--第二章作业1--习题答案
- 深入讲解WebView——下
- [置顶] Android屏幕适配全攻略(最权威的官方适配指导)
- [Android][转]Android(安卓)View绘制13问13答
- Android之ConstraintLayout
- Android彻底退出应用程序(转)
- Android(安卓)Context 是什么?
- Android(安卓)studio 注释模板
- Android中第五大组件详解