Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 【翻譯】通訊錄數據的存取(一)——獲取通訊錄列表

【翻譯】通訊錄數據的存取(一)——獲取通訊錄列表

編輯:關於Android編程

有選擇性的翻譯自:https://developer.android.com/training/contacts-provider/index.html
Contacts Provider是用戶通信信息倉庫,包含通訊錄應用程序和社交網絡應用程序的數據。我們可以通過直接調用ContactsResolver的方法或直接發送調用通訊錄應用程序的intent來獲取Contacts Provider提供的信息。

獲取通訊錄列表

根據以下三種類型匹配獲取列表:
- 匹配通信人姓名
- 匹配某類型數據,如電話號碼
- 匹配任意數據

在使用之前需要申請如下權限:


匹配通信人姓名

實現方案是將字符串與通訊錄提供者(Contact Provider)的ContactsContract.Contacts表的一個或多個通信人的姓名進行匹配。
這裡以ListView來展示結果為例介紹整個過程。

定義ListView的布局

創建整體布局文件: res/layout/contacts_list_view.xml:


該文件使用內置的Android ListView組件android:id/list.
然後定義每一項的布局文件 contacts_list_item.xml :


該文件使用內置的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,
        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 {
    ...
    // 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 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 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 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 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 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
        );
    }

至此,匹配任意類型數據的搜索實現已結束。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved