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

通讯录数据的存取

有选择性的翻译自: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 column
private static final int CONTACT_ID_INDEX = 0;
// The column index for the LOOKUP_KEY column
private 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
        );
    }

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

时间: 2024-11-08 12:59:25

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

React Native之获取通讯录信息并实现类通讯录列表(ios android)

React Native之获取通讯录信息并实现类通讯录列表(ios android) 一,需求分析 1,获取通讯录信息,筛选出通讯录里有多少好友在使用某个应用. 2,获取通讯录信息,实现类通讯录,可拨打电话 发短信等. 二,技术介绍与配置 2.1 技术介绍 1,react-native-contacts地址:https://www.npmjs.com/package/react-native-contacts 2,下载安装:npm install react-native-contacts --

利用AddressBook.framework框架获取iOS系统通讯录数据

此方法是使用AddressBook.framework框架获取通讯录信息 第一步.在info.plist文件里面配置访问权限 第二步.导入头文件 1 #import <AddressBook/AddressBook.h> 2 #import <AddressBookUI/AddressBookUI.h> 第三步.获取通讯录数据 1 - (void)getSystemAddress{ 2 3 //新建一个通讯录类 4 ABAddressBookRef addressBooks = n

获取通讯录信息在iOS8.0和9.0的区别

这篇文章主要介绍在iOS8.0和iOS9.0有界面和无界面 获取通讯录信息的方法.下面我们逐个介绍  首先我们要明确 无论是 iOS8.0和iOS9.0在有界面的情况下都不需要授权 1.iOS8.0 有界面的操作  需要导入 AddressBookUI 静态库 遵循 ABPeoplePickerNavigationControllerDelegate协议 需要注意的是我们在不用桥接的情况下要注意内存泄露问题 #import "ViewController.h" #import <

Android手机获取通讯录联系人信息及短信广播实现

现在越来越多的android应用在注册时都要用到手机号码,通过获取手机验证码来完成注册.也有不少应用提供了手机通讯录备份功能,获得你的允许后把你的通讯录中的手机号码保存到服务器中,你要的时候又可以down下来,比如微信,QQ等就有这们的功能.那我们怎么样获取用户通讯录中的手机号码呢?Android已经为我们做好了准备: ---------------------------------获取通讯录联系人信息----------------------------------------------

Android获取通讯录并上传(包含通讯录加密)

好久没更新文章了,近期在做通讯录上传,把它分享出来,送给需要的朋友. 写了一个通讯录工具类,直接放代码吧,关键位置通过注释来解释. 这个工具类包含通讯录获取,加密,然后上传操作.看不懂的可以留言 import android.database.Cursor; import android.os.AsyncTask; import android.provider.ContactsContract; import android.util.Base64; import com.demo.alfar

Android 获取通讯录联系人

public static Map<String,String> getAllCallRecords(Context context) { Map<String,String> temp = new HashMap<String, String>(); Cursor c = context.getContentResolver().query( ContactsContract.Contacts.CONTENT_URI, null, null, null, Contac

iOS 获取通讯录权限的时机

建议将获取通讯录权限的代码放到 -(void)viewDidAppear:(BOOL)animated 或 -(void)viewWillAppear:(BOOL)animated 假如放在 viewDidLoad  可能会是app启动进程中止.无法显示出界面

ios 获取通讯录的所有信息

iOS获取通讯录全部信息 ABAddressBookRef addressBook = ABAddressBookCreate(); CFArrayRef results = ABAddressBookCopyArrayOfAllPeople(addressBook); for(int i = 0; i < CFArrayGetCount(results); i++) { ABRecordRef person = CFArrayGetValueAtIndex(results, i); //读取f

关于Android下Delphi XE7获取通讯录的问题

在网上感到一篇高人写的代码XE6下获取通讯录的,但是弄到XE7不能用了,原因是TJavaObjectArray变成泛型了,弄了半天没有搞懂TJavaObjectArray怎么使用,老是出错,不过研究了下Androidapi.JNI.Provider下的源代码,发现操作Android数据库的几个函数,坐下记录 下面是我修改的可以获取到数据库用户的代码: uses FMX.Helpers.Android, Androidapi.JNI.JavaTypes, Androidapi.JNI.Graphi