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))); } } }
最後效果圖如下。可以實現聯繫人的添加,刪除和修改功能: