ContentProvider扩展之管理系统联系人
我们都知道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("刪除联系人") .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 ? "刪除成功!" : "刪除失敗!", 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))); } } }
最后效果图如下。可以实现联系人的添加,删除和修改功能: