> 编写:[spencer198711](https://github.com/spencer198711) - 原文:[http://developer.android.com/training/contacts-provider/retrieve-details.html](http://developer.android.com/training/contacts-provider/retrieve-details.html)
这一课展示了如何取得一个联系人的详细信息,比如email地址、电话号码等。当使用者去获取联系人信息的时候,这些信息正是他们所查找的。我们可以给他们关于一个联系人的所有信息,或者仅仅显示一个特定的数据类型,比如email地址。
这一课假设你已经获取到了一个用户所选取的联系人的[ContactsContract.Contacts](http://developer.android.com/reference/android/provider/ContactsContract.Contacts.html)数据项。在[获取联系人名字](#)那一课展示了如何获取联系人列表。
### 获取联系人的所有详细信息
为了取得一个联系人的所有详情,查找[ContactsContract.Data](http://developer.android.com/reference/android/provider/ContactsContract.Data.html)表中包含联系人[LOOKUP_KEY](http://developer.android.com/reference/android/provider/ContactsContract.ContactsColumns.html#LOOKUP_KEY)列的任意行。因为Contacts Provider隐式地连接了ContactsContract.Contacts表和ContactsContract.Data表,所以这个[LOOKUP_KEY](http://developer.android.com/reference/android/provider/ContactsContract.ContactsColumns.html#LOOKUP_KEY)列在ContactsContract.Data表中是可用的。关于[LOOKUP_KEY](http://developer.android.com/reference/android/provider/ContactsContract.ContactsColumns.html#LOOKUP_KEY)列,在[获取联系人名字](#)那一课有详细的描述。
> **Note:**检索一个联系人的所有信息会降低设备的性能,因为这需要检索ContactsContract.Data表的所有列。在使用这种方法之前,请认真考虑对性能影响。
### 请求权限
为了能够读Contacts Provider,我们的应用必须拥有[READ_CONTACTS](http://developer.android.com/reference/android/Manifest.permission.html#READ_CONTACTS)权限。为了请求这个权限,需要在manifest文件的中添加如下子节点:
~~~
<uses-permission android:name="android.permission.READ_CONTACTS" />
~~~
### 设置查询映射
根据一行数据的数据类型,它可能会使用很多列或者只使用几列。另外,数据会根据不同的数据类型而出现在不同的列中。为了确保能够获取所有数据类型的所有可能的数据列,需要在查询映射中添加所有列的名字。如果要把Cursor绑定到ListView,记得要获取Data._ID,否则的话,界面绑定就不会起作用。同时也需要获取[Data.MIMETYPE](http://developer.android.com/reference/android/provider/ContactsContract.DataColumns.html#MIMETYPE)列,这样才能识别我们获取到的每一行数据的数据类型。例如:
~~~
private static final String PROJECTION =
{
Data._ID,
Data.MIMETYPE,
Data.DATA1,
Data.DATA2,
Data.DATA3,
Data.DATA4,
Data.DATA5,
Data.DATA6,
Data.DATA7,
Data.DATA8,
Data.DATA9,
Data.DATA10,
Data.DATA11,
Data.DATA12,
Data.DATA13,
Data.DATA14,
Data.DATA15
};
~~~
这个查询映射使用了ContactsContract.Data类中定义的列名字,去获取ContactsContract.Data表中一行的所有数据列。
我们也可以使用由ContactsContract.Data或其子类定义的列常量去设置查询映射。需要注意的是,从SYNC1到SYNC4的数据列是sync adapter同步数据所使用的,它们的值对我们没有意义。
### 定义查询标准
为查询选择子句定义一个常量,一个包含查询选择参数的数组,以及一个保存查询选择值的变量。使用Contacts.LOOKUP_KEY列去查找这个联系人。例如:
~~~
// Defines the selection clause
private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
// Defines the array to hold the search criteria
private String[] mSelectionArgs = { "" };
/*
* Defines a variable to contain the selection value. Once you
* have the Cursor from the Contacts table, and you've selected
* the desired row, move the row's LOOKUP_KEY value into this
* variable.
*/
private String mLookupKey;
~~~
在查询选择表达式中使用 “?”占位符,确保了搜索是由绑定生成而不是由SQL编译生成。这种方法消除了恶意SQL注入的可能性。
### 定义排序顺序
定义在查询结果Cursor中希望的排序顺序。按照Data.MIMETYPE去排序,可以让特定数据类型的所有行排列在一起。这种形式的查询排序参数让所有具有email的行排在一起,让所有具有电话的行排在一起……例如:
~~~
/*
* Defines a string that specifies a sort order of MIME type
*/
private static final String SORT_ORDER = Data.MIMETYPE;
~~~
> **Note:**一些数据类型不使用子类型,所以不能按照子类型来排序。作为替代方法,我们不得不遍历返回的Cursor,去判定当前行的数据类型,为那些使用子类型的数据行保存数据。当读取完cursor后,我们可以根据子类型去排序每一个数据类型并显示结果。
### 初始化查询loader
永远在后台线程中去检索Contacts Provider(或者其他content provider)的数据。使用Loader框架中的LoaderManager类和LoaderManager.LoaderCallbacks在后台去做获取数据的工作。
当我们已经准备好去获取数据行,需要通过调用initLoader()方法去初始化loader框架。传递一个Integer类型的标识符给initLoader()方法,这个标识符会传递给LoaderManager.LoaderCallbacks方法。当在一个应用中使用多个loader时,这个标识符能够帮助我们区分它们。
以下的代码片段展示了如何初始化loader框架:
~~~
public class DetailsFragment extends Fragment implements
LoaderManager.LoaderCallbacks<Cursor> {
...
// Defines a constant that identifies the loader
DETAILS_QUERY_ID = 0;
...
/*
* Invoked when the parent Activity is instantiated
* and the Fragment's UI is ready. Put final initialization
* steps here.
*/
@Override
onActivityCreated(Bundle savedInstanceState) {
...
// Initializes the loader framework
getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
~~~
### 实现onCreateLoader()方法
实现onCreateLoader()方法。loader框架会在我们调用initLoader()方法后立即调用onCreateLoader()方法。这个方法会返回一个CursorLoader对象。由于搜索的是ContactsContract.Data表,所以需要使用常量Data.CONTENT_URI作为内容URI。例如:
~~~
@Override
public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
// Choose the proper action
switch (loaderId) {
case DETAILS_QUERY_ID:
// Assigns the selection parameter
mSelectionArgs[0] = mLookupKey;
// Starts the query
CursorLoader mLoader =
new CursorLoader(
getActivity(),
Data.CONTENT_URI,
PROJECTION,
SELECTION,
mSelectionArgs,
SORT_ORDER
);
...
}
~~~
### 实现onLoadFinished()方法和onLoaderReset()方法
实现onLoadFinished()方法。当Contacts Provider返回查询结果的时候,loader框架会调用onLoadFinished()方法。例如:
~~~
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
switch (loader.getId()) {
case DETAILS_QUERY_ID:
/*
* Process the resulting Cursor here.
*/
}
break;
...
}
}
~~~
当loader框架检测到结果集Cursor所对应的数据已经发生变化的时候,会调用onLoaderReset()方法。这时,需要通过把Cursor设置为null来移除对已经存在Cursor对象的引用。否则,loader框架就不会销毁旧的Cursor对象,从而导致内存泄漏。例如:
~~~
@Override
public void onLoaderReset(Loader<Cursor> loader) {
switch (loader.getId()) {
case DETAILS_QUERY_ID:
/*
* If you have current references to the Cursor,
* remove them here.
*/
}
break;
}
~~~
### 获取联系人的特定类型的信息
获取联系人的特定类型的信息,例如所有的email信息,跟获取联系人的所有详细信息类似。下面的内容是在[获取联系人的所有详细信息]()列出的代码的基础上作出的修改:
查询映射
修改查询映射使得能够针对特定的数据类型去获取列。同时需要修改查询映射,来把在ContactsContract.CommonDataKinds子类中定义的列常量与数据类型对应起来。
查询选择
修改查询选择子句去搜索特定类型的MIMETYPE值。
排序顺序
由于仅仅搜索一种类型的详细数据,所以不需要将返回的Cursor按照Data.MIMETYPE进行分组。
这些修改将会在下面的小节中详细描述。
### 设置查询映射
使用ContactsContract.CommonDataKinds的特定类型子类所定义的列名称常量,定义我们想要获取的数据列。如果我们打算把Cursor绑定到ListView,确保要获取`_ID`列。例如,为了获取email数据,需要定义以下数据映射:
~~~
private static final String[] PROJECTION =
{
Email._ID,
Email.ADDRESS,
Email.TYPE,
Email.LABEL
};
~~~
需要注意的是,这个查询映射使用在ContactsContract.CommonDataKinds.Email类中定义的列名称,来替代ContactsContract.Data类中定义的列名称。使用email类型的列名称使得代码更具可读性。
在查询映射中,我们也可以使用ContactsContract.CommonDataKinds子类所定义的其他数据列。
### 定义查询标准
根据我们想要找的特定联系人的LOOKUP_KEY和联系人详细信息的Data.MIMETYPE定义一个搜索表达式,去获取数据。把MIMETYPE的值从头到尾用单引号括住,否则的话,content provider将会把这个常量当成变量名而不是字符串。因为我们使用的是常量,而不是用户提供的值,所以这里不需要使用占位符。例如:
~~~
/*
* Defines the selection clause. Search for a lookup key
* and the Email MIME type
*/
private static final String SELECTION =
Data.LOOKUP_KEY + " = ?" +
" AND " +
Data.MIMETYPE + " = " +
"'" + Email.CONTENT_ITEM_TYPE + "'";
// Defines the array to hold the search criteria
private String[] mSelectionArgs = { "" };
~~~
### 定义排序规则
为查询返回的[Cursor](http://developer.android.com/reference/android/database/Cursor.html)定义一个排序规则。由于是检索特定的数据类型,删除根据[MIMETYPE](http://developer.android.com/reference/android/provider/ContactsContract.DataColumns.html#MIMETYPE)来排序的部分。而如果查询的详细数据类型包含子类型,可以根据这个子类型去排序。例如,对于email数据,我们可以根据[Email.TYPE](http://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.CommonColumns.html#TYPE)排序:
~~~
private static final String SORT_ORDER = Email.TYPE + " ASC ";
~~~
- 序言
- Android入门基础:从这里开始
- 建立第一个App
- 创建Android项目
- 执行Android程序
- 建立简单的用户界面
- 启动其他的Activity
- 添加ActionBar
- 建立ActionBar
- 添加Action按钮
- 自定义ActionBar的风格
- ActionBar的覆盖层叠
- 兼容不同的设备
- 适配不同的语言
- 适配不同的屏幕
- 适配不同的系统版本
- 管理Activity的生命周期
- 启动与销毁Activity
- 暂停与恢复Activity
- 停止与重启Activity
- 重新创建Activity
- 使用Fragment建立动态的UI
- 创建一个Fragment
- 建立灵活动态的UI
- Fragments之间的交互
- 数据保存
- 保存到Preference
- 保存到文件
- 保存到数据库
- 与其他应用的交互
- Intent的发送
- 接收Activity返回的结果
- Intent过滤
- Android分享操作
- 分享简单的数据
- 给其他App发送简单的数据
- 接收从其他App返回的数据
- 给ActionBar增加分享功能
- 分享文件
- 建立文件分享
- 分享文件
- 请求分享一个文件
- 获取文件信息
- 使用NFC分享文件
- 发送文件给其他设备
- 接收其他设备的文件
- Android多媒体
- 管理音频播放
- 控制音量与音频播放
- 管理音频焦点
- 兼容音频输出设备
- 拍照
- 简单的拍照
- 简单的录像
- 控制相机硬件
- 打印
- 打印照片
- 打印HTML文档
- 打印自定义文档
- Android图像与动画
- 高效显示Bitmap
- 高效加载大图
- 非UI线程处理Bitmap
- 缓存Bitmap
- 管理Bitmap的内存
- 在UI上显示Bitmap
- 使用OpenGL ES显示图像
- 建立OpenGL ES的环境
- 定义Shapes
- 绘制Shapes
- 运用投影与相机视图
- 添加移动
- 响应触摸事件
- 添加动画
- View间渐变
- 使用ViewPager实现屏幕侧滑
- 展示卡片翻转动画
- 缩放View
- 布局变更动画
- Android网络连接与云服务
- 无线连接设备
- 使得网络服务可发现
- 使用WiFi建立P2P连接
- 使用WiFi P2P服务
- 执行网络操作
- 连接到网络
- 管理网络
- 解析XML数据
- 高效下载
- 为网络访问更加高效而优化下载
- 最小化更新操作的影响
- 避免下载多余的数据
- 根据网络类型改变下载模式
- 云同步
- 使用备份API
- 使用Google Cloud Messaging
- 解决云同步的保存冲突
- 使用Sync Adapter传输数据
- 创建Stub授权器
- 创建Stub Content Provider
- 创建Sync Adpater
- 执行Sync Adpater
- 使用Volley执行网络数据传输
- 发送简单的网络请求
- 建立请求队列
- 创建标准的网络请求
- 实现自定义的网络请求
- Android联系人与位置信息
- Android联系人信息
- 获取联系人列表
- 获取联系人详情
- 使用Intents修改联系人信息
- 显示联系人头像
- Android位置信息
- 获取最后可知位置
- 获取位置更新
- 显示位置地址
- 创建和监视地理围栏
- Android可穿戴应用
- 赋予Notification可穿戴特性
- 创建Notification
- 在Notifcation中接收语音输入
- 为Notification添加显示页面
- 以Stack的方式显示Notifications
- 创建可穿戴的应用
- 创建并运行可穿戴应用
- 创建自定义的布局
- 添加语音功能
- 打包可穿戴应用
- 通过蓝牙进行调试
- 创建自定义的UI
- 定义Layouts
- 创建Cards
- 创建Lists
- 创建2D-Picker
- 创建确认界面
- 退出全屏的Activity
- 发送并同步数据
- 访问可穿戴数据层
- 同步数据单元
- 传输资源
- 发送与接收消息
- 处理数据层的事件
- Android TV应用
- 创建TV应用
- 创建TV应用的第一步
- 处理TV硬件部分
- 创建TV的布局文件
- 创建TV的导航栏
- 创建TV播放应用
- 创建目录浏览器
- 提供一个Card视图
- 创建详情页
- 显示正在播放卡片
- 帮助用户在TV上探索内容
- TV上的推荐内容
- 使得TV App能够被搜索
- 使用TV应用进行搜索
- 创建TV游戏应用
- 创建TV直播应用
- TV Apps Checklist
- Android企业级应用
- Ensuring Compatibility with Managed Profiles
- Implementing App Restrictions
- Building a Work Policy Controller
- Android交互设计
- 设计高效的导航
- 规划屏幕界面与他们之间的关系
- 为多种大小的屏幕进行规划
- 提供向下和横向导航
- 提供向上和历史导航
- 综合:设计样例 App
- 实现高效的导航
- 使用Tabs创建Swipe视图
- 创建抽屉导航
- 提供向上的导航
- 提供向后的导航
- 实现向下的导航
- 通知提示用户
- 建立Notification
- 当启动Activity时保留导航
- 更新Notification
- 使用BigView风格
- 显示Notification进度
- 增加搜索功能
- 建立搜索界面
- 保存并搜索数据
- 保持向下兼容
- 使得你的App内容可被Google搜索
- 为App内容开启深度链接
- 为索引指定App内容
- Android界面设计
- 为多屏幕设计
- 兼容不同的屏幕大小
- 兼容不同的屏幕密度
- 实现可适应的UI
- 创建自定义View
- 创建自定义的View类
- 实现自定义View的绘制
- 使得View可交互
- 优化自定义View
- 创建向后兼容的UI
- 抽象新的APIs
- 代理至新的APIs
- 使用旧的APIs实现新API的效果
- 使用版本敏感的组件
- 实现辅助功能
- 开发辅助程序
- 开发辅助服务
- 管理系统UI
- 淡化系统Bar
- 隐藏系统Bar
- 隐藏导航Bar
- 全屏沉浸式应用
- 响应UI可见性的变化
- 创建使用Material Design的应用
- 开始使用Material Design
- 使用Material的主题
- 创建Lists与Cards
- 定义Shadows与Clipping视图
- 使用Drawables
- 自定义动画
- 维护兼容性
- Android用户输入
- 使用触摸手势
- 检测常用的手势
- 跟踪手势移动
- Scroll手势动画
- 处理多触摸手势
- 拖拽与缩放
- 管理ViewGroup中的触摸事件
- 处理键盘输入
- 指定输入法类型
- 处理输入法可见性
- 兼容键盘导航
- 处理按键动作
- 兼容游戏控制器
- 处理控制器输入动作
- 支持不同的Android系统版本
- 支持多个控制器
- Android后台任务
- 在IntentService中执行后台任务
- 创建IntentService
- 发送工作任务到IntentService
- 报告后台任务执行状态
- 使用CursorLoader在后台加载数据
- 使用CursorLoader执行查询任务
- 处理查询的结果
- 管理设备的唤醒状态
- 保持设备的唤醒
- 制定重复定时的任务
- Android性能优化
- 管理应用的内存
- 代码性能优化建议
- 提升Layout的性能
- 优化layout的层级
- 使用include标签重用layouts
- 按需加载视图
- 使得ListView滑动顺畅
- 优化电池寿命
- 监测电量与充电状态
- 判断与监测Docking状态
- 判断与监测网络连接状态
- 根据需要操作Broadcast接受者
- 多线程操作
- 在一个线程中执行一段特定的代码
- 为多线程创建线程池
- 启动与停止线程池中的线程
- 与UI线程通信
- 避免出现程序无响应ANR
- JNI使用指南
- 优化多核处理器(SMP)下的Android程序
- Android安全与隐私
- Security Tips
- 使用HTTPS与SSL
- 为防止SSL漏洞而更新Security
- 使用设备管理条例增强安全性
- Android测试程序
- 测试你的Activity
- 建立测试环境
- 创建与执行测试用例
- 测试UI组件
- 创建单元测试
- 创建功能测试
- 術語表