【翻译】通讯录数据的存取(一)——获取通讯录列表

来源:互联网 发布:兰州大学网络教育网址 编辑:程序博客网 时间:2024/05/18 03:04

通讯录数据的存取

有选择性的翻译自:https://developer.android.com/training/contacts-provider/index.html
Contacts Provider是用户通信信息仓库,包含通讯录应用程序和社交网络应用程序的数据。我们可以通过直接调用ContactsResolver的方法或直接发送调用通讯录应用程序的intent来获取Contacts Provider提供的信息。

目录

  • 通讯录数据的存取
      • 目录
    • 获取通讯录列表
      • 匹配通信人姓名
        • 定义ListView的布局
        • 定义Fragment展示通信人列表
        • 定义全局变量
        • 初始化Fragment
        • 给ListView设置CursorAdapter
        • 设置点击通讯录列表的监听器
        • 定义一个规划projection
        • 定义Cursor列的索引值常量
        • 定义选择标准
        • 定义onItemClick方法
        • 初始化CursorLoader
        • 实现onCreateLoader
        • 实现onLoadFinished和onLoadReset
      • 匹配某类型数据
        • 选择数据类型和表
        • 定义一个规划projection
        • 定义选择标准
        • 实现onCreateLoader
      • 匹配任意数据
        • 去除选择标准
        • 实现onCreateLoader

获取通讯录列表

根据以下三种类型匹配获取列表:
- 匹配通信人姓名
- 匹配某类型数据,如电话号码
- 匹配任意数据

在使用之前需要申请如下权限:

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

匹配通信人姓名

实现方案是将字符串与通讯录提供者(Contact Provider)的ContactsContract.Contacts表的一个或多个通信人的姓名进行匹配。
这里以ListView来展示结果为例介绍整个过程。

定义ListView的布局

创建整体布局文件: res/layout/contacts_list_view.xml:

<?xml version="1.0" encoding="utf-8"?><ListView xmlns:android="http://schemas.android.com/apk/res/android"          android:id="@android:id/list"          android:layout_width="match_parent"          android:layout_height="match_parent"/>

该文件使用内置的Android ListView组件android:id/list.
然后定义每一项的布局文件 contacts_list_item.xml :

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"          android:id="@android:id/text1"          android:layout_width="match_parent"          android:layout_height="wrap_content"          android:clickable="true"/>

该文件使用内置的Android TextView组件:android:text1.

接下来定义使用上述UI展示通信人列表的代码。

定义Fragment展示通信人列表

为了更好的帮助开发者查询Contacts Provider,Android框架提供了名为ContactsContract的协议类,它定义了用来存取provider的常量和方法。使用该类,你无需定义URI,表名或列名等常量。
我们使用CursorLoader 从provider获取数据, 因此必须实现接口:LoaderManager.LoaderCallbacks. 另外,实现AdapterView.OnItemClickListener 以获取用户在搜索列表中选择的联系人信息。相应代码如下:

...import android.support.v4.app.Fragment;import android.support.v4.app.LoaderManager.LoaderCallbacks;import android.widget.AdapterView;...public class ContactsFragment extends Fragment implements        LoaderManager.LoaderCallbacks<Cursor>,        AdapterView.OnItemClickListener {

定义全局变量

   ...    /*     * Defines an array that contains column names to move from     * the Cursor to the ListView.     */    @SuppressLint("InlinedApi")    private final static String[] FROM_COLUMNS = {            Build.VERSION.SDK_INT                    >= Build.VERSION_CODES.HONEYCOMB ?                    Contacts.DISPLAY_NAME_PRIMARY :                    Contacts.DISPLAY_NAME    };    /*     * Defines an array that contains resource ids for the layout views     * that get the Cursor column contents. The id is pre-defined in     * the Android framework, so it is prefaced with "android.R.id"     */    private final static int[] TO_IDS = {           android.R.id.text1    };    // Define global mutable variables    // Define a ListView object    ListView mContactsList;    // Define variables for the contact the user selects    // The contact's _ID value    long mContactId;    // The contact's LOOKUP_KEY    String mContactKey;    // A content URI for the selected contact    Uri mContactUri;    // An adapter that binds the result Cursor to the ListView    private SimpleCursorAdapter mCursorAdapter;    ...

初始化Fragment

添加空的结构体,然后在onCreateView()中加载Fragment对象的UI。如下:

  // Empty public constructor, required by the system    public ContactsFragment() {}    // A UI Fragment must inflate its View    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        // Inflate the fragment layout        return inflater.inflate(R.layout.contact_list_fragment,            container, false);    }

给ListView设置CursorAdapter

使用SimpleCursorAdapter 将搜索结果与ListView关联。如下:

  public void onActivityCreated(Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        ...        // Gets the ListView from the View list of the parent activity        mContactsList =            (ListView) getActivity().findViewById(R.layout.contact_list_view);        // Gets a CursorAdapter        mCursorAdapter = new SimpleCursorAdapter(                getActivity(),                R.layout.contact_list_item,                null,                FROM_COLUMNS, TO_IDS,                0);        // Sets the adapter for the ListView        mContactsList.setAdapter(mCursorAdapter);    }

设置点击通讯录列表的监听器

当展示了搜索列表后,我们会允许用户点击某一联系人以做进一步的操作。如,当用户点击联系人后,在地图上显示联系人的地址。为做到这一点,首先定义一个实现了AdapterView.OnItemClickListener 接口的Fragment,就像在定义Fragment展示通信人列表*这一节中所讲的。
然后在onActivityCreated()中调用setOnItemClickListener()将监听器与ListView做关联。如:

public void onActivityCreated(Bundle savedInstanceState) {        ...        // Set the item click listener to be the current fragment.        mContactsList.setOnItemClickListener(this);        ...    }

定义一个规划(projection)

定义一个常量,包含所有你打算返回的列名。如:

...@SuppressLint("InlinedApi")private static final String[] PROJECTION =        {            Contacts._ID,            Contacts.LOOKUP_KEY,            Build.VERSION.SDK_INT                    >= Build.VERSION_CODES.HONEYCOMB ?                    Contacts.DISPLAY_NAME_PRIMARY :                    Contacts.DISPLAY_NAME        };

其中,列名Contacts._ID会在SimpleCursorAdapter 绑定过程中用到。Contacts._ID和 LOOKUP_KEY 一起用来构造用户选取的联系人的内容URI。 而ListView在展示联系人列表时会用到联系人姓名。该列在Android 3.0(API 11)及以后,列名叫Contacts.DISPLAY_NAME_PRIMARY,之前叫Contacts.DISPLAY_NAME。

定义Cursor列的索引值常量

获取某cursor某列的数据时,需要该列在Cursor中的列索引值。因为该索引值使用的是上一节定义的规划中的列的顺序,因此可以把列的索引值定义为常量。

// The column index for the _ID columnprivate static final int CONTACT_ID_INDEX = 0;// The column index for the LOOKUP_KEY columnprivate static final int LOOKUP_KEY_INDEX = 1;

定义选择标准

使用文字表达式指出要搜索的数据列,尽管该表达式可以包含参数值,推荐使用占位符“?”来代替参数值。使用“?”可以保证搜索通过连接而不是SQL编译来生成,避免恶意SQL注入的可能性。
使用变量来表示待搜索的内容,即SQL中替换“?”的内容。代码如下:

 // Defines the text expression    @SuppressLint("InlinedApi")    private static final String SELECTION =            Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?            Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :            Contacts.DISPLAY_NAME + " LIKE ?";    // Defines a variable for the search string    private String mSearchString;    // Defines the array to hold values that replace the ?    private String[] mSelectionArgs = { mSearchString };

定义onItemClick()方法

实现上一节的监听器,如下:

  @Override    public void onItemClick(        AdapterView<?> parent, View item, int position, long rowID) {        // Get the Cursor        Cursor cursor = parent.getAdapter().getCursor();        // Move to the selected contact        cursor.moveToPosition(position);        // Get the _ID value        mContactId = getLong(CONTACT_ID_INDEX);        // Get the selected LOOKUP KEY        mContactKey = getString(CONTACT_KEY_INDEX);        // Create the contact's content Uri        mContactUri = Contacts.getLookupUri(mContactId, mContactKey);        /*         * You can use mContactUri as the content URI for retrieving         * the details for a contact.         */    }

初始化CursorLoader

既然使用CursorLoader 来获取数据,必须初始化控制异步获取数据的后台线程和其他变量。在onActivityCreated()方法中做初始化,该函数会在Fragment的UI出现之前被调用。如下:

public class ContactsFragment extends Fragment implements        LoaderManager.LoaderCallbacks<Cursor> {    ...    // Called just before the Fragment displays its UI    @Override    public void onActivityCreated(Bundle savedInstanceState) {        // Always call the super method first        super.onActivityCreated(savedInstanceState);        ...        // Initializes the loader        getLoaderManager().initLoader(0, null, this);

实现onCreateLoader()

该方法会在我们调用initLoader()后立即被系统调用。
在onCreateLoader()中,设置搜索串模式。字符串中插入:“%”表示0或更多个字符串,“_”代表一个字符。如,模式”%Jefferson%”会匹配“Thomas Jefferson”和”Jefferson Davis”. 至于内容URI,使用Contacts.CONTENT_URI, 它指向整个表。代码如下:

  ...    @Override    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {        /*         * Makes search string into pattern and         * stores it in the selection array         */        mSelectionArgs[0] = "%" + mSearchString + "%";        // Starts the query        return new CursorLoader(                getActivity(),                Contacts.CONTENT_URI,                PROJECTION,                SELECTION,                mSelectionArgs,                null        );    }

实现onLoadFinished()和onLoadReset()

Loader框架在Contacts Provider返回搜索结果时调用onLoadFinished().在该方法中,将结果Cursor放入SimpleCursorAdapter中,这样会自动使用搜索结果更新ListView:

    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {        // Put the result Cursor in the adapter for the ListView        mCursorAdapter.swapCursor(cursor);    }

onLoaderReset()方法会在loader框架发现Cursor结果包含旧数据时被调用。在该方法中,需要清除SimpleCursorAdapter对已有Cursor的引用,否则loader框架不会回收该Cursor,会引起内存泄漏。

@Override    public void onLoaderReset(Loader<Cursor> loader) {        // Delete the reference to the existing Cursor        mCursorAdapter.swapCursor(null);    }

至此,已经讲完了搜索通信人姓名并将搜索结果在ListView中展现的所有关键点。

匹配某类型数据

实现这一类型的数据获取,首先需要像上一节一样实现以下代码:

  • 申请度通讯录的权限
  • 定义ListView的布局
  • 定义Fragment展示通信人列表
  • 定义全局变量
  • 初始化Fragment
  • 给ListView设置CursorAdapter
  • 设置点击通讯录列表的监听器
  • 定义Cursor列的索引值常量
  • 定义onItemClick方法
  • 初始化CursorLoader
  • 实现onLoadFinished和onLoadReset

除上述步骤外,需要额外的代码来获取匹配某些类型数据的通讯录信息。接下来是与上一节实现方面有不同的步骤。

选择数据类型和表

搜索某类型的具体数据,需要知道该类型对应的MIME类型。所有的数据类型都有一个唯一的MIME类型值,该值是ContactsContract.CommonDataKinds 中与数据类型匹配的子类中定义的常量CONTENT_ITEM_TYPE。如,Email数据对应的子类是ContactsContract.CommonDataKinds.Email, 而它的MIME类型是Email.CONTENT_ITEM_TYPE常量.
选择完数据类型后,选择该使用哪个表。使用ContactsContract.Data表,该表中定义或继承了所有需要查询的列名或如何排序等常量。

定义一个规划(projection)

可以选择ContactsContract.Data或它继承的类中的一个或多个列。Contacts Provider在返回结果前会对ContactsContract.Data表和其他表做一个隐式的连接。如下:

 @SuppressLint("InlinedApi")    private static final String[] PROJECTION =        {            /*             * The detail data row ID. To make a ListView work,             * this column is required.             */            Data._ID,            // The primary display name            Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?                    Data.DISPLAY_NAME_PRIMARY :                    Data.DISPLAY_NAME,            // The contact's _ID, to construct a content URI            Data.CONTACT_ID,            // The contact's LOOKUP_KEY, to construct a content URI            Data.LOOKUP_KEY         };

定义选择标准

使用如下数据构造一个搜索语句:
- 包含待搜索字符串信息的列的名字 根据搜索类型的不同而变化,因此需要先找出与搜索类型对应的ContactsContract.CommonDataKinds 的子类,然后选择该子类的列名。如,使用列名Email.ADDRESS来搜索email地址。
- 搜索字符串 在搜索语句中使用“?”来代替。
- 包含MIME类型的列名 该值是固定值:Data.MIMETYPE.
- 搜索类型对应的MIME值 ContactsContract.CommonDataKinds 中与数据类型匹配的子类中定义的常量CONTENT_ITEM_TYPE。如,Email数据对应的子类是ContactsContract.CommonDataKinds.Email, 而它的MIME类型是Email.CONTENT_ITEM_TYPE常量. 需要使用单引号将该常量括起来,否则provider会把它当作变量名而不是常量。
代码如下:

 /*     * Constructs search criteria from the search string     * and email MIME type     */    private static final String SELECTION =            /*             * Searches for an email address             * that matches the search string             */            Email.ADDRESS + " LIKE ? " + "AND " +            /*             * Searches for a MIME type that matches             * the value of the constant             * Email.CONTENT_ITEM_TYPE. Note the             * single quotes surrounding Email.CONTENT_ITEM_TYPE.             */            Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";

接下来定义包含搜索参数的变量:

 String mSearchString;    String[] mSelectionArgs = { "" };

实现onCreateLoader

与上一节不同的是,这里的内容URI,使用Data.CONTENT_URI.如下:

 @Override    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {        // OPTIONAL: Makes search string into pattern        mSearchString = "%" + mSearchString + "%";        // Puts the search string into the selection criteria        mSelectionArgs[0] = mSearchString;        // Starts the query        return new CursorLoader(                getActivity(),                Data.CONTENT_URI,                PROJECTION,                SELECTION,                mSelectionArgs,                null        );    }

至此,匹配某类型数据的搜索实现已结束。

匹配任意数据

返回匹配某数据的联系人列表,而不管该数据是名字,Email地址,通信地址或电话号码等。
实现这一类型的数据获取,首先需要像上一节一样实现以下代码:

  • 申请度通讯录的权限
  • 定义ListView的布局
  • 定义Fragment展示通信人列表
  • 定义全局变量
  • 初始化Fragment
  • 给ListView设置CursorAdapter
  • 设置点击通讯录列表的监听器
  • 定义一个规划(projection)
  • 定义Cursor列的索引值常量
  • 定义onItemClick方法
  • 初始化CursorLoader
  • 实现onLoadFinished和onLoadReset

接下来是与上一节实现方面有不同的步骤。

去除选择标准

不用定义SELECTION常量或mSelectionArgs变量。

实现onCreateLoader

不再需要将字符串转换为模式,因为Contacts Provider会自动为你做。使用Contacts.CONTENT_FILTER_URI作为基础URI, 通过调用Uri.withAppendedPath()将待搜索字符串追加到上述URI. 使用处理后的URI来搜索任意类型的数据。代码如下:

@Override    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {        /*         * Appends the search string to the base URI. Always         * encode search strings to ensure they're in proper         * format.         */        Uri contentUri = Uri.withAppendedPath(                Contacts.CONTENT_FILTER_URI,                Uri.encode(mSearchString));        // Starts the query        return new CursorLoader(                getActivity(),                contentUri,                PROJECTION,                null,                null,                null        );    }

至此,匹配任意类型数据的搜索实现已结束。

0 0
原创粉丝点击