我的CSDN博文链接:http://blog.csdn.net/diyangxia/article/details/44919163
一、跳转系统通讯录
普通的联系人列表,无法选择联系人或回调,只能查看详情
class="java"> 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、进一步优化:侧边栏字母的值跟随列表数据值变化而变化