我的CSDN博文鏈接:http://blog.csdn.net/diyangxia/article/details/44919163
一、跳轉系統通訊錄
普通的聯系人列表,無法選擇聯系人或回調,只能查看詳情
Intent intent = new Intent();
intent.setClassName("com.android.contacts",
"com.android.contacts.activities.PeopleActivity");
startActivity(intent);

二、選擇聯系人回調結果
跳轉至選擇聯系人列表:
Intent intent = new Intent(); intent.setAction(Intent.ACTION_PICK); // intent.setData(Contacts.People.CONTENT_URI); intent.setData(ContactsContract.Contacts.CONTENT_URI); startActivityForResult(intent, 100);
回調結果處理:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 100 && resultCode == RESULT_OK) {
Log.i("TAG", "回調開始");
Uri data2 = data.getData();// content://contacts/people/2
Cursor cursor = getContentResolver().query(data2, null, null, null,
null);
if (cursor.moveToFirst()) {
String name = cursor
.getString(cursor
.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Log.i("TAG", "姓名:" + name);
Cursor phones = getContentResolver()
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = "
+ cursor.getString(cursor
.getColumnIndex(ContactsContract.Contacts._ID)),
null, null);
if (phones.moveToFirst()) {
String phoneNumber = phones
.getString(phones
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Log.i("TAG", "電話:" + phoneNumber);
tv1.setText("姓名:" + name + "\n電話:" + phoneNumber);
}
phones.close();
}
cursor.close();
}
}
友情提醒,到此為止的測試環境都是在GenyMotion模擬器上,在真機上的結果是可能會出現重復的數據

三、自定義聯系人界面
getContentResolver獲取到的聯系人列表,默認是按照創建時間排序,在實際使用中,我們希望按姓名排序,有以下兩種方法:
1、使用query方法中的projection參數和sortorder參數來實現排序,比較簡單方便
/**
* see http://blog.chinaunix.net/uid-26930580-id-4137246.html
* @param listMembers
*/
public void getAllContact(List<ContactData> listMembers) {
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
Cursor cursor = getContentResolver().query(uri, MYPROJECTION,
null, null, Phone.SORT_KEY_PRIMARY);
try {
if (cursor.moveToFirst()) {
do {
ContactData contact = new ContactData();
String contact_phone = cursor.getString(2);
String name = cursor.getString(0);
String sortKey = getSortKey(cursor.getString(1));
int contact_id = cursor.getInt(3);
contact.name = name;
contact.sortKey = sortKey;
contact.phone = contact_phone;
contact.setId(contact_id);
if (name != null)
listMembers.add(contact);
} while (cursor.moveToNext());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
cursor.close();
}
}
/**
* 獲取sort key的首個字符,如果是英文字母就直接返回,否則返回#。
*
* @param sortKeyString
* 數據庫中讀取出的sort key
* @return 英文字母或者#
*/
private static String getSortKey(String sortKeyString) {
String key = sortKeyString.substring(0, 1).toUpperCase();
if (key.matches("[A-Z]")) {
return key;
}
return "#";
}
這里面的ContactData就是自定義的實體類,包括幾個常用屬性
public int id; public String name; public String phone; public String sortKey;
A list of which columns to return. Passing null will return all columns, which is inefficient.這是第二個參數projection的解釋,其實就是定義要返回list類型,為null就返回所有字段
private static final String[] MYPROJECTION = new String[] {
Phone.DISPLAY_NAME, Phone.SORT_KEY_PRIMARY, Phone.NUMBER,
Phone.CONTACT_ID };
備注:由於genymotion模擬器的聯系人無法輸入中文,所以暫時換成了海馬玩模擬器,所以和上面數據不相同

2、其實如果只是模擬器跑應用的話,上面的就夠了,但是在真機中還有sim卡中的聯系人,如果還用上面那個方法,那么獲取的將會是所有的數據,包括手機本地的以及sim卡中的聯系人,這樣就會有重復的數據,我的解決方法就是先去判斷手機的sim卡狀態,如果狀態良好的話就直接取sim卡的聯系人數據,如果取得的sim卡的聯系人數據為空,那么再調用上面那個方法獲取全部聯系人,如果沒有sim卡的話,也調用上面那個方法獲取全部聯系人。
只取sim卡中聯系人並進行漢字排序的方法:
private void getSIMContacts(List<ContactData> listMembers) {
ContentResolver resolver = getContentResolver();
// 獲取Sims卡聯系人
Uri uri = Uri.parse("content://icc/adn");
Cursor phoneCursor = resolver.query(uri, null, null,
null, null);
try {
if (phoneCursor.moveToFirst()) {
do {
ContactData data = new ContactData();
// 得到手機號碼
String phoneNumber = phoneCursor.getString(1); //在測試機上索引為2,華為榮耀上索引為1
// 當手機號碼為空的或者為空字段 跳過當前循環
if (phoneNumber.equals(" ") || phoneNumber == null)
continue;
// 得到聯系人名稱
String contactName = phoneCursor.getString(0);//在測試機上索引為1,華為榮耀上索引為0,原因應該是測試機聯系人的表結構多了一個自增長的id
if (contactName.equals(" ") || contactName == null)
continue;
data.name = contactName;
data.phone = phoneNumber;
if (contactName != null)
listMembers.add(data);
} while (phoneCursor.moveToNext());
//中文排序
Comparator comp = new Mycomparator();
Collections.sort(listMembers, comp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
phoneCursor.close();
}
}
//通訊錄按中文拼音排序,英文放在最后面
class Mycomparator implements Comparator {
public int compare(Object o1, Object o2) {
ContactData c1 = (ContactData) o1;
ContactData c2 = (ContactData) o2;
Comparator cmp = Collator.getInstance(java.util.Locale.CHINA);
return cmp.compare(c1.name, c2.name);
}
}
四、后續優化
1、優化了sim卡和手機通訊錄聯系人重復的處理方法,使用第三步第二小步的方法是有點繁瑣,所以這里就使用了先取到所有的聯系人數據,然后對list進行去重處理;最關鍵的地方就是記得重寫實體類的equals方法,不然使用list.contains(contactData)時會一直返回false
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if (obj instanceof ContactData) {
ContactData u = (ContactData) obj;
return this.name.equals(u.name) && this.phone.equals(phone)
&& this.sortKey.equals(sortKey);
}
return super.equals(obj);
}
list去重的方法其實有很多,這里列舉兩個高效的,主要是代碼簡潔不會打亂原有順序,用set就會出現無序的list了
List<ContactData> removeDuplicateData(List<ContactData> cList) {
List<ContactData> cList2 = new ArrayList<ContactData>();
for (ContactData data : cList) {
if (Collections.frequency(cList2, data) < 1)
cList2.add(data);
}
return cList2;
}
List<ContactData> removeDuplicateData2(List<ContactData> cList) {
List<ContactData> cList2 = new ArrayList<ContactData>();
for (int i = 0; i < cList.size(); i++) {
if (!cList2.contains(cList.get(i)))
cList2.add(cList.get(i));
}
return cList2;
}
2、上滑下拉字母動畫,有這樣一個頂的效果,可以參考PinnedHeaderListView

下載了github上的源碼,根據他的example稍微該了下adapter,主要還是要自己生成一個list數據傳過去,生成通訊錄list關鍵代碼如下:
for (int i = 0; i < cList2.size(); i++) {
ParentContactData pData = new ParentContactData();
pData.setLetter(cList2.get(i).sortKey);
List<ContactData> dList = new ArrayList<ContactListActivity.ContactData>();
dList.add(cList2.get(i));
while ((i + 1) < cList2.size()
&& cList2.get(i + 1).sortKey.equals(cList2.get(i).sortKey)) {
dList.add(cList2.get(i + 1));
i++;
}
pData.setChildlist(dList);
listData.add(pData);
}
主要是首字母的計算,使用了for循環中的while循環進行判斷,如果滿足條件就添加到之前的字母list中,否則跳出while循環
3、右側添加sideBar,字母索引,這里面借用了Android 實現ListView的A-Z字母排序和過濾搜索功能,實現漢字轉成拼音這篇博文中的SideBar源碼,還有一個獲取首字母索引的方法
/**
* 根據分類的首字母的Char ascii值獲取其第一次出現該首字母的位置
* @return 父索引值,即ABCD等的索引0-26
*/
public int getPositionForSection(int section) {
for (int i = 0; i < mListData.size(); i++) {
String sortStr = mListData.get(i).getLetter();
char firstChar = sortStr.toUpperCase().charAt(0);
if (firstChar == section) {
return i;
}
}
return -1;
}
獲取到這個索引值后,在列表界面的代碼中還不能直接調用mListView.setSelection()方法,因為上面這個方法返回的索引值只是父列表的索引值,而setSelection方法要的參數是相對於整個Listview的索引值,所以還得再加工計算一下
public int getAllPosition(int section) {
int allPosition = 0;
for (int i = 0; i < section; i++) {
allPosition = allPosition + mListData.get(i).childlist.size();
}
allPosition = allPosition + section;
return allPosition;
}
效果圖如下:

4、添加簡單搜索,其實和滑動側邊SideBar的效果一樣的,只是換一種方法,目前還未實現模糊搜索聯系人的方法,其中的頂部搜索的輸入框右邊的刪除圖片借用了上一步中提到的博文里的思路,漢字拼音的轉化也是如此。
目前最終效果圖如下:

5、進一步優化:側邊欄字母的值跟隨列表數據值變化而變化
