1、概述

最近由于项目需求,需要做一个查看手机通讯录,并且取出相应的数据。类似于下图:

用到的一个主要的知识点:SectionIndexer——能够有效地帮助我们对分组进行控制,由于SectionIndexer是一个接口,你可以自定义一个子类来实现SectionIndexer,

不过自己再写一个SectionIndexer的实现太麻烦了,这里我们直接使用Android提供好

的实现AlphabetIndexer,用它来实现联系人分组功能已经足够了。AlphabetIndexer的构造函数需要传入三个参数,第一个参数是cursor,

第二个参数是sortedColumnIndex整型,第三个参数是alphabet字符串。

其中cursor就是把我们从数据库中查出的游标传进去,

sortedColumnIndex就是指明我们是使用哪一列进行排序的,

而alphabet则是指定字母表排序规则,比如:"ABCDEFGHIJKLMNOPQRSTUVWXYZ"。

有了AlphabetIndexer,我们就可以通过它的getPositionForSection和getSectionForPosition方法,

找出当前位置所在的分组,和当前分组所在的位置,

从而实现类似于系统联系人的分组导航和挤压动画效果。

2、效果图

大家可以清晰的看到,滑动界面,右侧的指示也会随着页面的变换而变换,
按住右侧,是按照字母来查询,由于这里是使用的genymotion,不能输入中文汉字,
所以都是英文的联系人,中文同样的效果。点击查询,字母的显示条就会消失,
这里使用的是模糊查询,只要名字中有你输入的关键字都会显示出来。
源码点击下载

3、实现

下面我们就来开始实现,新建一个Android项目,命名为Contacts。首先我们还是先来完成布局文件,打开或新建activity_main.xml作为程序的主布局文件,代码会长一点,由于要实现右侧的A-Z的滑动栏,中间有一大部分的代码是类似的,这里就不贴出来了,感兴趣的朋友可以下载整个项目。
然后新建一个contact_item.xml的布局,这个布局用于在ListView中的每一行进行填充,代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:background="#ffffff"><LinearLayoutandroid:id="@+id/sort_key_layout"android:layout_width="fill_parent"android:layout_height="18dip"android:layout_marginRight="15dp"android:background="#64a300"><TextViewandroid:id="@+id/sort_key"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:layout_marginLeft="10dip"android:textColor="#ffffff"android:textSize="13sp" /></LinearLayout><LinearLayoutandroid:id="@+id/name_layout"android:layout_width="fill_parent"android:layout_height="60dip"android:orientation="vertical"><TextViewandroid:id="@+id/name"android:layout_width="wrap_content"android:layout_height="0dp"android:layout_weight="1"android:gravity="center_vertical"android:textColor="#303030"android:layout_marginLeft="15dp"android:text="李三"android:textSize="20sp" /><TextViewandroid:layout_width="match_parent"android:layout_height="1dp"android:layout_marginLeft="15dp"android:layout_marginRight="15dp"android:background="#4064a300" /></LinearLayout></LinearLayout>

在这个布局文件中,首先是放入了一个和前面完成一样的分组布局,因为不仅界面头部需要展示分组,在每个分组内的第一个无素之前都需要展示分组布局。然后是加入一个简单的LinearLayout,包含一个TextView用于显示联系人姓名。

这样我们的布局文件就全部写完了,下面开始来真正地实现功能。

先从简单的开始,新建一个PlayerInfo实体类:

package com.example.contactsdemo;import java.io.Serializable;/** 用户信息* auth:liyachao* date:2015/4/6*/
public class PlayerInfo {private String playerName;private String playerPhone;/*** 排序字母*/private String sortKey;public String getSortKey() {return sortKey;}public void setSortKey(String sortKey) {this.sortKey = sortKey;}public String getPlayerPhone() {return playerPhone;}public void setPlayerPhone(String playerPhone) {this.playerPhone = playerPhone;}public String getPlayerName() {return playerName;}public void setPlayerName(String playerName) {this.playerName = playerName;}}

这个实体类很简单,只包含了联系人姓名、排序键和联系人电话。

接下来完成联系人列表适配器的编写,新建一个ContactAdapter类继承自ArrayAdapter,加入如下代码:

package com.example.contactsdemo;import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.SectionIndexer;
import android.widget.TextView;import java.util.List;/*** 联系人列表适配器。** @author guolin*/
public class ContactAdapter extends BaseAdapter {/*** 需要渲染的item布局文件*/private int resource;private Context context;private List<PlayerInfo> players;private boolean flag = true;/*** 字母表分组工具*/private SectionIndexer mIndexer;public ContactAdapter(Context context, int textViewResourceId, List<PlayerInfo> players) {resource = textViewResourceId;this.context = context;this.players = players;}@Overridepublic int getCount() {return players.size();}@Overridepublic PlayerInfo getItem(int position) {return players.get(position);}@Overridepublic long getItemId(int position) {return position;}public void dataChanged(List<PlayerInfo> players) {this.players = players;notifyDataSetChanged();flag = false;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {PlayerInfo contact = getItem(position);LinearLayout layout = null;if (convertView == null) {layout = (LinearLayout) LayoutInflater.from(context).inflate(resource, null);} else {layout = (LinearLayout) convertView;}TextView name = (TextView) layout.findViewById(R.id.name);LinearLayout sortKeyLayout = (LinearLayout) layout.findViewById(R.id.sort_key_layout);TextView sortKey = (TextView) layout.findViewById(R.id.sort_key);name.setText(contact.getPlayerName());if (flag) {int section = mIndexer.getSectionForPosition(position);if (position == mIndexer.getPositionForSection(section)) {sortKey.setText(contact.getSortKey());sortKeyLayout.setVisibility(View.VISIBLE);} else {sortKeyLayout.setVisibility(View.GONE);}} else {sortKeyLayout.setVisibility(View.GONE);}return layout;}/*** 给当前适配器传入一个分组工具。** @param indexer*/public void setIndexer(SectionIndexer indexer) {mIndexer = indexer;flag = true;}}

上面的代码中,最重要的就是getView方法,在这个方法中,我们首先判断是查找类型还是滑动页面类型,如果是查找类型,我们将头部的指示条就隐藏,如果不是再做判断,使用SectionIndexer的getSectionForPosition方法,通过当前的position值拿到了对应的section值,然后再反向通过刚刚拿到的section值,调用getPositionForSection方法,取回新的position值。如果当前的position值和新的position值是相等的,那么我们就可以认为当前position的项是某个分组下的第一个元素,我们应该将分组布局显示出来。

最后我们来编写程序的主界面,打开或新建MainActivity作为程序的主界面,代码如下所示:

package com.example.contactsdemo;import java.util.ArrayList;
import java.util.List;import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AlphabetIndexer;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;/*** chang date:2015/4/5 by liyachao*/
public class MainActivity extends Activity {private ListView lv;private EditText edittext;/*** 联系人总人数*/private int contactNumber;/*** 分组的布局*/private LinearLayout titleLayout;/*** 分组上显示的字母*/private TextView title;private ArrayList<PlayerInfo> allPlayerInfos;/*** 联系人列表适配器*/private ContactAdapter adapter;/*** 用于进行字母表分组*/private AlphabetIndexer indexer;private RelativeLayout sectionToastLayout;private TextView sectionToastText;private LinearLayout alphabetLayout;/*** A-Z的集合*/private List<TextView> alphabetList;/*** 定义字母表的排序规则*/private String alphabet = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ";private int lastFirstVisibleItem = -1;/*** 电话号码**/private static final int PHONES_NUMBER_INDEX = 1;/*** 联系人显示名称**/private static final int PHONES_DISPLAY_NAME_INDEX = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);setControl();}private void setControl() {alphabetList = new ArrayList<TextView>();setAlphabetData();titleLayout = (LinearLayout) findViewById(R.id.title_layout1);title = (TextView) findViewById(R.id.title);sectionToastLayout = (RelativeLayout) findViewById(R.id.section_toast_layout);sectionToastText = (TextView) findViewById(R.id.section_toast_text);alphabetLayout = (LinearLayout) findViewById(R.id.alphabet_layout);lv = (ListView) findViewById(R.id.lv_select_contact);edittext = (EditText) findViewById(R.id.edittext);allPlayerInfos = getContactInfos();adapter = new ContactAdapter(this, R.layout.contact_item, allPlayerInfos);edittext.setHint("搜索" + contactNumber + "位联系人");adapter.setIndexer(indexer);if (allPlayerInfos.size() > 0) {setupContactsListView();setAlpabetListener();}/*** 向listview设置点击事件*/lv.setOnItemClickListener(new MyOnItemClickListener());/*** 向edittext设置监听事件*/edittext.addTextChangedListener(new MyTextWatcher());}/*** 根据填写的关键字在电话簿里寻找相关信息** @param name* @return*/private ArrayList<PlayerInfo> searchItem(String name) {ArrayList<PlayerInfo> mSearchList = new ArrayList<PlayerInfo>();for (int i = 0; i < allPlayerInfos.size(); i++) {int index = allPlayerInfos.get(i).getPlayerName().indexOf(name);// 存在匹配的数据if (index != -1) {mSearchList.add(allPlayerInfos.get(i));}}return mSearchList;}/*** 获取系统的所有的联系人信息.** @return*/public ArrayList<PlayerInfo> getContactInfos() {allPlayerInfos = new ArrayList<PlayerInfo>();ContentResolver resolver = getContentResolver();Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;Cursor phoneCursor = resolver.query(uri,new String[]{Phone.DISPLAY_NAME, Phone.NUMBER, Phone.SORT_KEY_PRIMARY}, null, null, Phone.SORT_KEY_PRIMARY);if (phoneCursor.moveToFirst()) {do {//得到手机号码String phoneNumber = phoneCursor.getString(PHONES_NUMBER_INDEX);String sortKey = getSortKey(phoneCursor.getString(2));//得到联系人名称String contactName = phoneCursor.getString(PHONES_DISPLAY_NAME_INDEX);PlayerInfo playerInfo = new PlayerInfo();playerInfo.setPlayerName(contactName);playerInfo.setPlayerPhone(phoneNumber);playerInfo.setSortKey(sortKey);allPlayerInfos.add(playerInfo);} while (phoneCursor.moveToNext());}contactNumber = allPlayerInfos.size();startManagingCursor(phoneCursor);indexer = new AlphabetIndexer(phoneCursor, 2, alphabet);return allPlayerInfos;}/*** 获取sort key的首个字符,如果是英文字母就直接返回,否则返回#。** @param sortKeyString 数据库中读取出的sort key* @return 英文字母或者#*/private String getSortKey(String sortKeyString) {alphabetLayout.getHeight();String key = sortKeyString.substring(0, 1).toUpperCase();if (key.matches("[A-Z]")) {return key;}return "#";}/*** 为联系人ListView设置监听事件,* 根据当前的滑动状态来改变分组的显示位置,* 从而实现挤压动画的效果。* auth:liyachao*/private void setupContactsListView() {lv.setAdapter(adapter);lv.setOnScrollListener(new AbsListView.OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,int totalItemCount) {int section = indexer.getSectionForPosition(firstVisibleItem);int nextSecPosition = indexer.getPositionForSection(section + 1);setSortAlphabet(section);if (firstVisibleItem != lastFirstVisibleItem) {ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) titleLayout.getLayoutParams();params.topMargin = 0;titleLayout.setLayoutParams(params);title.setText(String.valueOf(alphabet.charAt(section)));}if (nextSecPosition == firstVisibleItem + 1) {View childView = view.getChildAt(0);if (childView != null) {int titleHeight = titleLayout.getHeight();int bottom = childView.getBottom();ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) titleLayout.getLayoutParams();if (bottom < titleHeight) {float pushedDistance = bottom - titleHeight;params.topMargin = (int) pushedDistance;titleLayout.setLayoutParams(params);} else {if (params.topMargin != 0) {params.topMargin = 0;titleLayout.setLayoutParams(params);}}}}lastFirstVisibleItem = firstVisibleItem;}});}/*** 设置默认的字幕背景和字体颜色*/private void setSortAlphabet(int section) {TextView tv;for (int i = 0; i < 27; i++) {if (section == i) {tv = alphabetList.get(section);tv.setTextColor(Color.parseColor("#ffffff"));tv.setBackgroundResource(R.drawable.text_bg_frame);} else {tv = alphabetList.get(i);tv.setTextColor(Color.parseColor("#303030"));tv.setBackgroundColor(Color.parseColor("#ffffff"));}tv.setGravity(Gravity.CENTER);}}/*** 设置字母表上的触摸事件,根据当前触摸的位置结合字母表的高度,* 计算出当前触摸在哪个字母上。* 当手指按在字母表上时,展示弹出式分组。手指离开字母表时,* 将弹出式分组隐藏。* auth:liyachao*/private void setAlpabetListener() {alphabetLayout.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {float alphabetHeight = alphabetLayout.getHeight();float y = event.getY();int sectionPosition = (int) ((y / alphabetHeight) / (1f / 27f));if (sectionPosition < 0) {sectionPosition = 0;} else if (sectionPosition > 26) {sectionPosition = 26;}String sectionLetter = String.valueOf(alphabet.charAt(sectionPosition));int position = indexer.getPositionForSection(sectionPosition);switch (event.getAction()) {case MotionEvent.ACTION_DOWN:alphabetList.get(sectionPosition).setTextColor(Color.parseColor("#ffffff"));alphabetList.get(sectionPosition).setBackgroundResource(R.drawable.text_bg_frame);sectionToastLayout.setVisibility(View.VISIBLE);sectionToastText.setText(sectionLetter);lv.setSelection(position);break;case MotionEvent.ACTION_MOVE:setSortAlphabet(sectionPosition);sectionToastText.setText(sectionLetter);lv.setSelection(position);break;default:setSortAlphabet(sectionPosition);sectionToastLayout.setVisibility(View.GONE);}return true;}});}class MyOnItemClickListener implements OnItemClickListener {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {String name = ((PlayerInfo) parent.getItemAtPosition(position)).getPlayerName() + "";String phone = ((PlayerInfo) parent.getItemAtPosition(position)).getPlayerPhone() + "";String str = name + "的电话为: " + phone;Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();}}class MyTextWatcher implements TextWatcher {@Overridepublic void beforeTextChanged(CharSequence s, int start, int count, int after) {}@Overridepublic void onTextChanged(CharSequence s, int start, int before, int count) {}@Overridepublic void afterTextChanged(Editable s) {String str = s.toString();if (str.equals("")) {adapter.dataChanged(allPlayerInfos);adapter.setIndexer(indexer);titleLayout.setVisibility(View.VISIBLE);} else {Log.i("tag", "name111: " + str);ArrayList<PlayerInfo> temp;temp = searchItem(str.trim());adapter.dataChanged(temp);titleLayout.setVisibility(View.GONE);}lv.setAdapter(adapter);}}private void setAlphabetData() {TextView jing = (TextView) findViewById(R.id.contact_);alphabetList.add(jing);TextView A = (TextView) findViewById(R.id.contact_A);alphabetList.add(A);TextView B = (TextView) findViewById(R.id.contact_B);alphabetList.add(B);TextView C = (TextView) findViewById(R.id.contact_C);alphabetList.add(C);TextView D = (TextView) findViewById(R.id.contact_D);alphabetList.add(D);TextView E = (TextView) findViewById(R.id.contact_E);alphabetList.add(E);TextView F = (TextView) findViewById(R.id.contact_F);alphabetList.add(F);TextView G = (TextView) findViewById(R.id.contact_G);alphabetList.add(G);TextView H = (TextView) findViewById(R.id.contact_H);alphabetList.add(H);TextView I = (TextView) findViewById(R.id.contact_I);alphabetList.add(I);TextView J = (TextView) findViewById(R.id.contact_J);alphabetList.add(J);TextView K = (TextView) findViewById(R.id.contact_K);alphabetList.add(K);TextView L = (TextView) findViewById(R.id.contact_L);alphabetList.add(L);TextView M = (TextView) findViewById(R.id.contact_M);alphabetList.add(M);TextView N = (TextView) findViewById(R.id.contact_N);alphabetList.add(N);TextView O = (TextView) findViewById(R.id.contact_O);alphabetList.add(O);TextView P = (TextView) findViewById(R.id.contact_P);alphabetList.add(P);TextView Q = (TextView) findViewById(R.id.contact_Q);alphabetList.add(Q);TextView R1 = (TextView) findViewById(R.id.contact_R);alphabetList.add(R1);TextView S = (TextView) findViewById(R.id.contact_S);alphabetList.add(S);TextView T = (TextView) findViewById(R.id.contact_T);alphabetList.add(T);TextView U = (TextView) findViewById(R.id.contact_U);alphabetList.add(U);TextView V = (TextView) findViewById(R.id.contact_V);alphabetList.add(V);TextView W = (TextView) findViewById(R.id.contact_W);alphabetList.add(W);TextView X = (TextView) findViewById(R.id.contact_X);alphabetList.add(X);TextView Y = (TextView) findViewById(R.id.contact_Y);alphabetList.add(Y);TextView Z = (TextView) findViewById(R.id.contact_Z);alphabetList.add(Z);}}

最后要记住,要在AndroidManifest.xml给出读取手机联系人的权限声明:

<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>  

Android手机通讯录相关推荐

  1. Android手机通讯录备份和恢复项目

    Android手机通讯录备份和恢复项目 附下载地址** 登录功能 注册功能 找回密码 修改密码 备份功能 恢复功能 恢复数据从服务器 导出为Excel文件 登录功能 注册功能 找回密码 同注册功能 修 ...

  2. android 添加通讯录联系人头像,Android 手机通讯录开发时给联系人添加头像失败的坑...

    Android 手机通讯录开发时给联系人添加头像失败的坑,在给联系人添加头像代码 Bitmap photo = ...; // 将 Bitmap 转为 byte[] byte[] bytes = Ls ...

  3. 一篇很好的关于Android的本科毕业论文《基于android手机通讯录的设计与实现毕业论文》转自百度

    本文转自: http://wenku.baidu.com/view/bb7dad58804d2b160b4ec058.html 相应的word文档csdn下载地址: http://download.c ...

  4. android备份手机号码,Android手机通讯录备份还原代码

    最近想写段Android程序玩玩.开发环境 Eclipse ,Android 2.2 开发环境搭建 1.先安装jdk 2.下载安装eclipse 3.下载安装android sdk 4.安装eclip ...

  5. Android手机通讯录解析

    相关图片上传麻烦,原文件到我的博客上传的文件里下载. 1.通讯录应用介绍 通讯录应用是Android自带的应用程序,我们看到此应用的时候,可能只认为这是一个应用,用数据库存储数据,但是实际上不是这样的 ...

  6. Android手机通讯录(上)

    可以实现添加联系人,删除联系人,修改联系人,查找号码,查询联系人信息等功能. 具体实现界面如图所示. 相关代码介绍如下: Android项目配置AndroidManifest.xml <?xml ...

  7. Android手机通讯录(下)

    接上一篇博客内容. 如果有人需要完整代码讲解,相关毕设论文,我后续也会更新. 手机通讯录程序编辑和添加联系人布局xml[/layout/deit.xml] <?xml version=" ...

  8. pta通讯录排序用python实现,python实现将android手机通讯录vcf文件转化为csv

    经常会遇到将手机通讯录导出到电脑并转化为在电脑中可编辑的情况,在网上搜索了很久当前不外乎两种处理方式.1.使用电脑的outlook的通讯簿功能,将手机导出的vcf文件导入到outlook的通讯录中,然 ...

  9. android手机通讯录格式转换,手机通讯录小技巧,安卓手机通讯录转iPhone并不难,换机必学...

    原标题:手机通讯录小技巧,安卓手机通讯录转iPhone并不难,换机必学 今年618年中大促销,国内的电商巨头们分分大减价,就连号称保价的苹果手机,过万iPhone XS MAX都跌入八千户里.相信很多 ...

最新文章

  1. Javascript DOM动态添加表格
  2. python mulit函数_python – 将函数应用于MultiIndex pandas.DataFrame列
  3. Google: 如何做code review?
  4. 排除MySQL中常见错误的实用招术
  5. python利用numpy存取文件
  6. python函数参数的部分求值方法
  7. Latex——伪代码算法
  8. 5、海康威视摄像头配置和初步测试
  9. jTopo的基本使用
  10. 6步解决win7局域网内传输慢的问题
  11. Foxmail的创建
  12. VC++实现禁止上网
  13. 联想台式机快捷键(F1~F12)取消按Fn,设置为标准功能键
  14. 微信公众号申请开通微信支付
  15. android网络权限动态,Android权限详解(含6.0动态申请)
  16. 程序通过命令行获取操作系统名称+版本+CPU名称等各种信息
  17. 多层神经网络 ——小批量梯度下降法
  18. 冒泡排序和纯指针的冒泡排序
  19. 嵌入式 linux下proc目录下的文件详解
  20. QT/C++——主窗口和事件处理

热门文章

  1. pandorabox php,Openwrt Pandorabox 挂载摄像头 定时拍照上传百度网盘,实现实时监控(优酷路由宝)...
  2. 小米2s刷原生安卓_小米2S升级安卓5.0原生ROM下载刷机教程
  3. 带你理解beta分布
  4. 即时通讯功能技术方案
  5. 教师资格证计算机科目有哪些内容,初中教师资格证考试科目及内容有哪些?
  6. Java多态(面试考点,不要因为基础而忽视)
  7. 【Qualcomm高通音频】如何区分配置ECM驻极体麦克风和MEMS硅麦克风
  8. 【洛谷P1486】郁闷的出纳员【树状数组】
  9. 怎么搭建网校系统,培训机构搭建专属网校平台源码
  10. 2021-2022-1 ACM集训队每周程序设计竞赛(5) - 问题 C: 剪切 - 题解