我们都知道ContentProvider是用来共享数据的,然而android本身就提供了大量的ContentProvider,例如联系人信息,系统的多媒体信息等,这些系统的ContentProvider都提供了供其他应用程序访问的Uri,开发者可以通过ContentResolver来调用系统的ContentProvider提供的insert()/update()/delete()/query()方法,从而实现自己的需求。
1、了解系统联系人的结构
(1)android系统对联系人管理提供了很多的Uri,其中用到最多的几个如下:
ContactsContract.Contacts.CONTENT_URI:管理联系人的Uri
ContactsContract.CommonDataKinds.Phone.CONTENT_URI:管理联系人电话的Uri
ContactsContract.CommonDataKinds.Email.CONTENT_URI:管理联系人邮箱的Uri
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI:管理联系人地址的Uri
我们可以根据系统提供的这些Uri去操作系统联系人信息,具体的还有很多CONTENT_URI,可以到ContactsContract.class文件中去查看源码。
(2)表结构
我们之前说过,ContentProvider是以表的形式来存放这些数据的,系统联系人也是这样,一般存放在data/data/com.android.providers.contacts/databases/contacts2.db中,我们可以通过adb shell命令行的方式来看,也可以将这个contacts2.db文件导出,通过sqlite工具来查看,这里为了方便我们导出来看一下。
找到对应的文件,然后选中点击右上角的导出按钮,将文件导出到系统中的某一个地方
将导出的文件用工具打开,我这里使用的是:Navicat Premium
表和视图:
我们经常用的到有:contacts、data、raw_contacts三张表:
contacts表:
_id :表的ID,主要用于其它表通过contacts 表中的ID可以查到相应的数据。
display_name: 联系人名称
photo_id:头像的ID,如果没有设置联系人头像,这个字段就为空
times_contacted:通话记录的次数
last_time_contacted: 最后的通话时间
lookup :是一个持久化的储存 因为用户可能会改名子 但是它改不了lookup
data表:
raw_contact_id:通过raw_contact_id可以找到 raw_contact表中相对的数据。
data1 到 data15 这里保存着联系人的信息 联系人名称 联系人电话号码 电子邮件 备注 等等
raw_contacts表:
version :版本号,用于监听变化
deleted :删除标志, 0为默认 1 表示这行数据已经删除
display_name : 联系人名称
last_time_contacts : 最后联系的时间
【说明:由于这些表的字段都特别多,截图不全,可以自己试着导出一份看看】
2、代码实现
(1)首先要在AndroidManifest.xml文件中配置对系统联系人的读写权限:
<!-- 添加操作联系人的权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
(2)布局文件
首先是列表展示页面:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="50dp"
android:gravity="center_vertical"
android:text="我的联系人"
android:textSize="25dp" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@android:color/darker_gray" >
<TextView
android:id="@+id/id"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="1"
android:text="ID"
android:gravity="center" />
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="1"
android:text="姓名"
android:gravity="center" />
<TextView
android:id="@+id/phone"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="1"
android:text="手机号码"
android:gravity="center" />
</LinearLayout>
<ListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:divider="#FF0000"
android:dividerHeight="1dp"
android:focusable="true"
android:minHeight="40dp"
android:footerDividersEnabled="false" >
</ListView>
</LinearLayout>
列表展示中需要用的listview页面布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="@+id/id"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="40dp"
/>
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="40dp"
/>
<TextView
android:id="@+id/phone"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="40dp"
/>
</LinearLayout>
添加联系人界面:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_gravity="center"
android:orientation="horizontal" >
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:gravity="center_vertical|right"
android:text="姓名:"
android:textSize="20dp" />
<EditText
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_gravity="center"
android:orientation="horizontal" >
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:gravity="center_vertical|right"
android:layout_marginRight="20dp"
android:text="手机号码:"
android:textSize="20dp" />
<EditText
android:id="@+id/phone"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="50dp"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal" >
<Button
android:id="@+id/save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="保存"
android:textSize="20sp"
/>
<Button
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="取消"
android:layout_marginLeft="30dp"
android:textSize="20sp"
/>
</LinearLayout>
</LinearLayout>
联系人详细界面:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:background="@android:color/darker_gray" >
<TextView
android:id="@+id/tv_name"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:background="@android:color/white"
android:gravity="center_vertical|right"
android:text="姓名:"
android:textSize="20sp" />
<TextView
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/tv_name"
android:layout_toRightOf="@+id/tv_name"
android:background="@android:color/white"
android:gravity="center_vertical"
android:text=""
android:textSize="20sp" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_name"
android:layout_marginTop="20dp"
android:background="@android:color/white"
android:gravity="center_vertical|right"
android:text="手机号:"
android:textSize="20sp" />
<TextView
android:id="@+id/et_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/tv_phone"
android:layout_toRightOf="@+id/tv_phone"
android:background="@android:color/white"
android:gravity="center_vertical"
android:text=""
android:textSize="20sp" />
<TextView
android:id="@+id/tv_email"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_phone"
android:layout_marginTop="20dp"
android:background="@android:color/white"
android:gravity="center_vertical|right"
android:text="Email:"
android:textSize="20sp" />
<TextView
android:id="@+id/et_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/tv_email"
android:layout_toRightOf="@+id/tv_email"
android:background="@android:color/white"
android:gravity="center_vertical"
android:text=""
android:textSize="20sp" />
<TextView
android:id="@+id/tv_address"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_email"
android:layout_marginTop="20dp"
android:background="@android:color/white"
android:gravity="center_vertical|right"
android:text="地址:"
android:textSize="20sp" />
<TextView
android:id="@+id/et_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/tv_address"
android:layout_toRightOf="@+id/tv_address"
android:background="@android:color/white"
android:gravity="center_vertical"
android:text=""
android:textSize="20sp" />
</RelativeLayout>
程序中需要用的menu菜单配置在menu文件中:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<group android:id="@+id/group1">
<item android:id="@+id/detail" android:title="详情"></item>
<item android:id="@+id/update" android:title="修改"></item>
<item android:id="@+id/delete" android:title="删除"></item>
</group>
</menu>
(3)Activity代码:
列表页面以及适配器Adapter
ContactsActivity.java
package com.demo.contentprovider;
import java.util.ArrayList;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import android.widget.Toast;
import com.demo.adapter.ContactsAdapter;
import com.demo.model.Contact;
/**
* 手机联系人操作
* @author yinbenyang
*/
public class ContactsActivity extends Activity {
private static final int ADD = 0;
private static final int REQUEST_ADD = 100;
// 存储联系人的列表
private ArrayList<Contact> contactList = null;
// 联系人适配器
private ContactsAdapter adapter;
private ListView listview;
// 弹出式菜单
public PopupMenu popupmenu = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contentprovider);
listview = (ListView) findViewById(R.id.listview);
init();
listview.setOnItemLongClickListener(new OnItemLongClickListenerImpl());
// registerForContextMenu(listview);
}
// ----------------------------------------------------选项菜单---------------------------------------------------
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
menu.add(0, ADD, 0, "添加");
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case ADD:
Intent intent = new Intent(this, AddContactActivity.class);
startActivityForResult(intent, REQUEST_ADD);
break;
default:
break;
}
return true;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
switch (requestCode) {
case REQUEST_ADD:
init();
break;
default:
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
// 初始化,获取联系人
private void init() {
ContentResolver resolver = getContentResolver();
/**
* ContactsContract.Contacts.CONTENT_URI:手机联系人的Uri:content://com.android
* .contacts/contacts
* sort_key_alt:它里面保存的是联系人名字的拼音字母,例如联系人名字是“李明”,则sort_key保存的是“LI李MING明”,
* 这样如果是按sort_key或sort_key_alt排序的话,就可以将联系人按顺序排列
*/
Cursor cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, "sort_key_alt");
contactList = new ArrayList<Contact>();
while (cursor.moveToNext()) {
Contact contact = new Contact();
String phoneNumber = null;
String name = cursor.getString(cursor
.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String id = cursor.getString(cursor
.getColumnIndex(ContactsContract.Contacts._ID));
Cursor phoneCursor = resolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = "
+ id, null, null);
while (phoneCursor.moveToNext()) {
phoneNumber = phoneCursor
.getString(phoneCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
}
contact.setId(Integer.parseInt(id));
contact.setName(name);
contact.setPhone(phoneNumber);
contactList.add(contact);
}
adapter = new ContactsAdapter(this, contactList);
listview.setAdapter(adapter);
}
private class OnItemLongClickListenerImpl implements
OnItemLongClickListener {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
final int position, long id) {
final ContentResolver resolver = getContentResolver();
popupmenu = new PopupMenu(ContactsActivity.this, listview);
popupmenu.getMenuInflater().inflate(R.menu.my_menu,
popupmenu.getMenu());
popupmenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.detail:
String id = contactList.get(position).getId() + "";
Intent intent = new Intent(ContactsActivity.this,
ContactDetailActivity.class);
intent.putExtra("id", id);
startActivity(intent);
break;
case R.id.update:
Toast.makeText(ContactsActivity.this, "执行修改操作",
Toast.LENGTH_SHORT).show();
break;
case R.id.delete:
new AlertDialog.Builder(ContactsActivity.this)
.setTitle("?h除联系人")
.setMessage("你确定要删除该联系人吗?")
.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
int i = resolver
.delete(ContactsContract.RawContacts.CONTENT_URI,
ContactsContract.Data._ID
+ " = "
+ contactList
.get(position)
.getId(),
null);
Toast.makeText(ContactsActivity.this,
i == 1 ? "?h除成功!" : "?h除失?。?quot;,
Toast.LENGTH_SHORT).show();
init();
}
})
.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
dialog.dismiss();
}
}).create().show();
break;
default:
break;
}
// Toast.makeText(ContactsActivity.this,
// "你点击了:" + item.getTitle(), Toast.LENGTH_SHORT)
// .show();
// popupmenu.dismiss();
return true;
}
});
popupmenu.show();
return true;
}
}
}
/**
* <!-- 联系人相关的uri --> content://com.android.contacts/contacts 操作的数据是联系人信息Uri
* content://com.android.contacts/data/phones 联系人电话Uri
* content://com.android.contacts/data/emails 联系人Email Uri
*/
ContactsAdapter.java
package com.demo.adapter;
import java.util.ArrayList;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.demo.contentprovider.R;
import com.demo.model.Contact;
/**
* 联系人的Adapter
*
* @author yinbenyang
*/
public class ContactsAdapter extends BaseAdapter {
private Context context;
private ArrayList<Contact> listContact;
public ContactsAdapter(Context context, ArrayList<Contact> listContact) {
this.context = context;
this.listContact = listContact;
}
@Override
public int getCount() {
return listContact.size();
}
@Override
public Object getItem(int position) {
return listContact.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = View
.inflate(context, R.layout.listview_contact, null);
holder = new ViewHolder();
holder.id = (TextView) convertView.findViewById(R.id.id);
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.phone = (TextView) convertView.findViewById(R.id.phone);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Contact con = listContact.get(position);
holder.id.setText(con.getId().toString());
holder.name.setText(con.getName());
holder.phone.setText(con.getPhone());
return convertView;
}
static class ViewHolder {
TextView id;
TextView name;
TextView phone;
}
}
添加联系人Activity:
AddContactActivity.java
package com.demo.contentprovider;
import android.app.Activity;
import android.content.ContentUris;
import android.content.ContentValues;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
/**
* 添加联系人信息
* @author yinbenyang
*
*/
public class AddContactActivity extends Activity implements OnClickListener{
private EditText name,phone;
private Button save,cancel;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.addcontact);
name = (EditText) findViewById(R.id.name);
phone = (EditText) findViewById(R.id.phone);
save = (Button) findViewById(R.id.save);
cancel = (Button) findViewById(R.id.cancel);
save.setOnClickListener(this);
cancel.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.save:
String _name = name.getText().toString().replaceAll(" ", "");
String _phone = phone.getText().toString().replaceAll(" ","");
ContentValues values = new ContentValues();
//首先向RawContacts.CONTENT_URI执行一个空值插入,目的是获取系统返回的rawContactId
Uri rawContactUri =getContentResolver().insert(RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
//往data表入姓名数据
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
//设置内容类型
values.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
//设置内容名字
values.put(StructuredName.GIVEN_NAME, _name);
getContentResolver().insert(
ContactsContract.Data.CONTENT_URI, values);
//往data表入电话数据
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
//设置电话号码
values.put(Phone.NUMBER, _phone);
//设置电话类型
values.put(Phone.TYPE, Phone.TYPE_MOBILE);
getContentResolver().insert(
ContactsContract.Data.CONTENT_URI, values);
this.finish();
break;
case R.id.cancel:
this.finish();
break;
default:
break;
}
}
}
联系人详细页面:
ContactDetailActivity.java
package com.demo.contentprovider;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.TextView;
/**
* 联系人详细信息
*
* @author yinbenyang
*
*/
public class ContactDetailActivity extends Activity {
private TextView et_name, et_phone, et_email, et_address;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.contactdetail);
et_name = (TextView) findViewById(R.id.et_name);
et_phone = (TextView) findViewById(R.id.et_phone);
et_email = (TextView) findViewById(R.id.et_email);
et_address = (TextView) findViewById(R.id.et_address);
Intent intent = getIntent();
String id = intent.getStringExtra("id");
ContentResolver resolver = getContentResolver();
// 查找姓名
Cursor nameCursor = resolver.query(
ContactsContract.Contacts.CONTENT_URI,
null,
ContactsContract.Contacts._ID + " = " + id,
null, null);
while(nameCursor.moveToNext()){
et_name.setText(nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)));
}
// 查找手机号
Cursor phoneCursor = resolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + id,
null, null);
while (phoneCursor.moveToNext()) {
et_phone.setText(phoneCursor.getString(phoneCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
}
// 查找邮箱
Cursor emailCursor = resolver.query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + id,
null, null);
while (emailCursor.moveToNext()) {
et_email.setText(emailCursor.getString(emailCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)));
}
// 查找地址
Cursor addressCursor = resolver.query(
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID
+ " = " + id, null, null);
while (addressCursor.moveToNext()) {
et_address
.setText(addressCursor.getString(addressCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.DATA)));
}
}
}
最后效果图如下。可以实现联系人的添加,删除和修改功能: