擷取聯絡人名單

本課程說明如何使用下列技巧,擷取資料與搜尋字串全部或部分相符的聯絡人清單:

比對聯絡人名稱
將搜尋字串與聯絡人名稱資料的全部或部分進行比對,藉此擷取聯絡人清單。聯絡人供應程式允許多個名稱相同的例項,因此這項技術可以傳回相符項目的清單。
比對特定類型的資料,例如電話號碼
將搜尋字串與特定類型的詳細資料資料 (例如電子郵件地址) 進行比對,藉此擷取聯絡人清單。舉例來說,這項技巧可讓您列出電子郵件地址與搜尋字串相符的所有聯絡人。
比對任何類型的資料
將搜尋字串與任何類型的詳細資料資料 (包括姓名、電話號碼、街道地址、電子郵件地址等) 進行比對,擷取聯絡人清單。舉例來說,這項技巧可讓您接受搜尋字串的任何類型資料,然後列出資料與字串相符的聯絡人。

注意:本課程中的所有範例都會使用 CursorLoader 從聯絡人提供者擷取資料。CursorLoader 會在 UI 執行緒以外的執行緒上執行查詢。這可確保查詢不會拖慢 UI 回應時間,並造成使用者體驗不佳。詳情請參閱 Android 訓練課程「 在背景載入資料」。

要求讀取提供者的權限

應用程式必須具備 READ_CONTACTS 權限,才能搜尋聯絡人供應程式。如要提出要求,請在資訊清單檔案中加入下列 <uses-permission> 元素,做為 <manifest> 的子項元素:

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

依名稱比對聯絡人並列出結果

這項技術會嘗試將搜尋字串與聯絡人提供者 ContactsContract.Contacts 資料表中的聯絡人名稱或聯絡人名稱進行比對。您通常會希望在 ListView 中顯示結果,讓使用者自行選擇相符的聯絡人。

定義 ListView 和項目版面配置

如要在 ListView 中顯示搜尋結果,您需要定義整個 UI (包括 ListView) 的主要版面配置檔案,以及定義一行 ListView 的項目版面配置檔案。舉例來說,您可以使用下列 XML 建立主要版面配置檔案 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"/>

此 XML 使用內建 Android ListView 小工具 android:id/list

以下列 XML 定義項目版面配置檔案 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"/>

此 XML 使用內建 Android TextView 小工具 android:text1

注意:本課程不會說明向使用者取得搜尋字串的 UI,因為您可能會希望間接取得字串。舉例來說,您可以讓使用者選擇搜尋與傳入簡訊中字串相符的聯絡人。

您編寫的兩個版面配置檔案定義了顯示 ListView 的使用者介面。下一步是編寫使用這個 UI 來顯示聯絡人清單的程式碼。

定義顯示聯絡人清單的片段

如要顯示聯絡人清單,請先定義由 Activity 載入的 Fragment。使用 Fragment 是比較有彈性的技術,因為您可以使用一個 Fragment 顯示清單,再用第二個 Fragment 顯示使用者從清單中選擇的聯絡人詳細資料。您可以使用這個方法,結合本課程介紹的其中一項技巧,以及「 擷取聯絡人的詳細資料」課程中的其中一種。

如要瞭解如何透過 Activity 使用一或多個 Fragment 物件,請參閱訓練課程 使用 Fragment 建構動態 UI

為協助您針對聯絡人供應程式編寫查詢,Android 架構提供名為 ContactsContract 的合約類別,定義用於存取供應器的實用常數和方法。使用這個類別時,您不必自行定義內容 URI、資料表名稱或資料欄的常數。如要使用這個類別,請加入以下陳述式:

Kotlin

import android.provider.ContactsContract

Java

import android.provider.ContactsContract;

由於程式碼使用 CursorLoader 從供應器擷取資料,因此您必須指定其實作載入器介面 LoaderManager.LoaderCallbacks。此外,為協助偵測使用者從搜尋結果清單中選取的聯絡人,請實作轉接程式介面 AdapterView.OnItemClickListener。例如:

Kotlin

...
import android.support.v4.app.Fragment
import android.support.v4.app.LoaderManager
import android.widget.AdapterView
...
class ContactsFragment :
        Fragment(),
        LoaderManager.LoaderCallbacks<Cursor>,
        AdapterView.OnItemClickListener {

Java

...
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 {

定義全域變數

定義程式碼其他部分使用的全域變數:

Kotlin

...
/*
 * Defines an array that contains column names to move from
 * the Cursor to the ListView.
 */
@SuppressLint("InlinedApi")
private val FROM_COLUMNS: Array<String> = arrayOf(
        if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)) {
            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
        } else {
            ContactsContract.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 val TO_IDS: IntArray = intArrayOf(android.R.id.text1)
...
class ContactsFragment :
        Fragment(),
        LoaderManager.LoaderCallbacks<Cursor>,
        AdapterView.OnItemClickListener {
    ...
    // Define global mutable variables
    // Define a ListView object
    lateinit var contactsList: ListView
    // Define variables for the contact the user selects
    // The contact's _ID value
    var contactId: Long = 0
    // The contact's LOOKUP_KEY
    var contactKey: String? = null
    // A content URI for the selected contact
    var contactUri: Uri? = null
    // An adapter that binds the result Cursor to the ListView
    private val cursorAdapter: SimpleCursorAdapter? = null

Java

    ...
    /*
     * 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 ?
                    ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
                    ContactsContract.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 contactsList;
    // Define variables for the contact the user selects
    // The contact's _ID value
    long contactId;
    // The contact's LOOKUP_KEY
    String contactKey;
    // A content URI for the selected contact
    Uri contactUri;
    // An adapter that binds the result Cursor to the ListView
    private SimpleCursorAdapter cursorAdapter;
    ...

注意:由於 Contacts.DISPLAY_NAME_PRIMARY 需要 Android 3.0 (API 版本 11) 以上版本,因此將應用程式的 minSdkVersion 設為 10 以下時,Android Studio 會產生 Android Lint 警告。如要關閉這項警示,請在 FROM_COLUMNS 定義前加上 @SuppressLint("InlinedApi") 註解。

初始化 Fragment

初始化 Fragment。新增 Android 系統所需的空白公開建構函式,並在回呼方法 onCreateView() 中加載 Fragment 物件的使用者介面。例如:

Kotlin

    // A UI Fragment must inflate its View
    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        // Inflate the fragment layout
        return inflater.inflate(R.layout.contact_list_fragment, container, false)
    }

Java

    // 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

設定將搜尋結果繫結至 ListViewSimpleCursorAdapter。如要取得顯示聯絡人的 ListView 物件,您必須使用 Fragment 的父項活動呼叫 Activity.findViewById()。呼叫 setAdapter() 時,請使用父項活動的 Context。例如:

Kotlin

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        ...
        // Gets the ListView from the View list of the parent activity
        activity?.also {
            contactsList = it.findViewById<ListView>(R.id.contact_list_view)
            // Gets a CursorAdapter
            cursorAdapter = SimpleCursorAdapter(
                    it,
                    R.layout.contact_list_item,
                    null,
                    FROM_COLUMNS, TO_IDS,
                    0
            )
            // Sets the adapter for the ListView
            contactsList.adapter = cursorAdapter
        }
    }

Java

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

設定所選聯絡人監聽器

顯示搜尋結果時,您通常會希望讓使用者選取單一聯絡人進行後續處理。舉例來說,使用者點選聯絡人後,即可在地圖顯示聯絡人的地址。如要提供這項功能,您必須先指定類別實作 AdapterView.OnItemClickListener,將目前的 Fragment 定義為點擊事件監聽器,如「定義顯示聯絡人清單的片段」一節所示。

如要繼續設定事件監聽器,請呼叫 onActivityCreated() 中的 setOnItemClickListener() 方法,將其繫結至 ListView。例如:

Kotlin

    fun onActivityCreated(savedInstanceState:Bundle) {
        ...
        // Set the item click listener to be the current fragment.
        contactsList.onItemClickListener = this
        ...
    }

Java

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

由於您指定目前的 FragmentListViewOnItemClickListener,因此現在必須實作其必要方法 onItemClick(),以便處理點擊事件。我們將在成功一節中說明。

定義投影

定義一個常數,其中包含要從查詢傳回的資料欄。ListView 中的每個項目都會顯示聯絡人的顯示名稱,包含聯絡人姓名的主要形式。在 Android 3.0 (API 版本 11) 以上版本中,此欄的名稱為 Contacts.DISPLAY_NAME_PRIMARY;在先前的版本中,名稱則為 Contacts.DISPLAY_NAME

SimpleCursorAdapter 繫結程序會使用 Contacts._ID 資料欄。系統會搭配使用 Contacts._IDLOOKUP_KEY 來為使用者選取的聯絡人建構內容 URI。

Kotlin

...
@SuppressLint("InlinedApi")
private val PROJECTION: Array<out String> = arrayOf(
        ContactsContract.Contacts._ID,
        ContactsContract.Contacts.LOOKUP_KEY,
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
        else
            ContactsContract.Contacts.DISPLAY_NAME
)

Java

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

        };

定義遊標資料欄索引的常數

如要從 Cursor 中的個別資料欄取得資料,您需要 Cursor 中的資料欄索引。您可以為 Cursor 欄的索引定義常數,因為索引與投影中資料欄名稱的順序相同。例如:

Kotlin

// The column index for the _ID column
private const val CONTACT_ID_INDEX: Int = 0
// The column index for the CONTACT_KEY column
private const val CONTACT_KEY_INDEX: Int = 1

Java

// The column index for the _ID column
private static final int CONTACT_ID_INDEX = 0;
// The column index for the CONTACT_KEY column
private static final int CONTACT_KEY_INDEX = 1;

指定選取條件

如要指定所需資料,請建立文字運算式和變數組合,向供應商說明要搜尋的資料欄和要尋找的值。

如果是文字運算式,請定義列出搜尋資料欄的常數。雖然這個運算式也可包含值,但建議您使用「?」預留位置來呈現值。擷取期間,預留位置會替換為陣列中的值。使用「?」做為預留位置可確保搜尋規格是透過繫結 (而非 SQL 編譯) 產生。這種做法可以避免惡意 SQL 插入。例如:

Kotlin

// Defines the text expression
@SuppressLint("InlinedApi")
private val SELECTION: String =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            "${ContactsContract.Contacts.DISPLAY_NAME_PRIMARY} LIKE ?"
        else
            "${ContactsContract.Contacts.DISPLAY_NAME} LIKE ?"
...
    // Defines a variable for the search string
    private val searchString: String = ...
    // Defines the array to hold values that replace the ?
    private val selectionArgs = arrayOf<String>(searchString)

Java

    // 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 searchString;
    // Defines the array to hold values that replace the ?
    private String[] selectionArgs = { searchString };

定義 onItemClick() 方法

在上一節中,您將為 ListView 設定項目點擊事件監聽器。現在,定義 AdapterView.OnItemClickListener.onItemClick() 方法,為事件監聽器實作動作:

Kotlin

    override fun onItemClick(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
        // Get the Cursor
        val cursor: Cursor? = (parent.adapter as? CursorAdapter)?.cursor?.apply {
            // Move to the selected contact
            moveToPosition(position)
            // Get the _ID value
            contactId = getLong(CONTACT_ID_INDEX)
            // Get the selected LOOKUP KEY
            contactKey = getString(CONTACT_KEY_INDEX)
            // Create the contact's content Uri
            contactUri = ContactsContract.Contacts.getLookupUri(contactId, mContactKey)
            /*
             * You can use contactUri as the content URI for retrieving
             * the details for a contact.
             */
        }
    }

Java

    @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
        contactId = cursor.getLong(CONTACT_ID_INDEX);
        // Get the selected LOOKUP KEY
        contactKey = cursor.getString(CONTACT_KEY_INDEX);
        // Create the contact's content Uri
        contactUri = Contacts.getLookupUri(contactId, mContactKey);
        /*
         * You can use contactUri as the content URI for retrieving
         * the details for a contact.
         */
    }

初始化載入器

由於您使用 CursorLoader 擷取資料,因此必須初始化背景執行緒和其他控制非同步擷取作業的變數。請如以下範例所示在 onCreate() 中執行初始化作業:

Kotlin

class ContactsFragment :
        Fragment(),
        LoaderManager.LoaderCallbacks<Cursor> {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        // Always call the super method first
        super.onCreate(savedInstanceState)
        ...
        // Initializes the loader
        loaderManager.initLoader(0, null, this)

Java

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

導入 onCreateLoader()

實作 onCreateLoader() 方法,載入器架構會在您呼叫 initLoader() 後立即呼叫。

onCreateLoader() 中設定搜尋字串模式。如要將字串轉換為模式,請插入「%」 (百分比) 字元來表示 0 個以上字元的序列,或插入「_」(底線) 字元來代表單一字元,或兩者皆是。舉例來說,「%Jefferson%」模式會同時符合「Thomas Jefferson」和「Jefferson Davis」

從方法傳回新的 CursorLoader。如果是內容 URI,請使用 Contacts.CONTENT_URI。這個 URI 是指整個資料表,如以下範例所示:

Kotlin

    ...
    override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
        /*
         * Makes search string into pattern and
         * stores it in the selection array
         */
        selectionArgs[0] = "%$mSearchString%"
        // Starts the query
        return activity?.let {
            return CursorLoader(
                    it,
                    ContactsContract.Contacts.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    selectionArgs,
                    null
            )
        } ?: throw IllegalStateException()
    }

Java

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

導入 onLoadFinished() 和 onLoaderReset()

實作 onLoadFinished() 方法。當聯絡人供應程式傳回查詢結果時,載入器架構會呼叫 onLoadFinished()。在這個方法中,將結果 Cursor 置於 SimpleCursorAdapter 中。系統會自動根據搜尋結果更新 ListView

Kotlin

    override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
        // Put the result Cursor in the adapter for the ListView
        cursorAdapter?.swapCursor(cursor)
    }

Java

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

載入器架構偵測到結果 Cursor 包含過時資料時,系統會叫用 onLoaderReset() 方法。刪除現有 CursorSimpleCursorAdapter 參照。否則,載入器架構不會回收 Cursor,導致記憶體流失。例如:

Kotlin

    override fun onLoaderReset(loader: Loader<Cursor>) {
        // Delete the reference to the existing Cursor
        cursorAdapter?.swapCursor(null)
    }

Java

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

    }

您現在已具備應用程式的索引鍵,會將搜尋字串與聯絡人名稱進行比對,接著在 ListView 中傳回結果。使用者可以按一下聯絡人名稱來選取聯絡人。 這會觸發監聽器,供您進一步處理聯絡人的資料。例如,您可以擷取聯絡人的詳細資料。如要瞭解相關做法,請繼續參閱下一堂「擷取聯絡人的詳細資料」。

如要進一步瞭解搜尋使用者介面,請參閱 API 指南的「建立搜尋介面」。

本課程的其餘部分會示範在聯絡人供應程式中尋找聯絡人的其他方法。

依特定類型的資料比對聯絡人

這項技巧可讓你指定要比對的資料類型。依名稱擷取是這類查詢的特定範例,但您也可以用名稱擷取與聯絡人相關聯的任何類型詳細資料。例如,您可以擷取含有特定郵遞區號的聯絡人。在這種情況下,搜尋字串必須與郵遞區號列中儲存的資料相符。

如要實作這種擷取類型,請先實作下列程式碼,如前述部分所示:

  • 要求讀取提供者的權限。
  • 定義 ListView 和項目版面配置。
  • 定義顯示聯絡人清單的片段。
  • 定義全域變數。
  • 初始化 Fragment。
  • 設定 ListView 的 CursorAdapter。
  • 設定所選聯絡人監聽器。
  • 定義遊標欄索引的常數。

    雖然您是從不同的資料表擷取資料,但投影中的資料欄順序相同,因此可為 Cursor 使用相同的索引。

  • 定義 onItemClick() 方法。
  • 初始化載入器。
  • 實作 onLoadFinished() 和 onLoaderReset()。

下列步驟說明的是將搜尋字串與特定類型詳細資料資料配對,並顯示結果所需的其他程式碼。

選擇資料類型和資料表

如要搜尋特定類型的詳細資料,您必須先瞭解該資料類型的自訂 MIME 類型值。每個資料類型都有專屬的 MIME 類型值,由與資料類型相關聯的 ContactsContract.CommonDataKinds 子類別中的常數 CONTENT_ITEM_TYPE 定義。子類別的名稱會指出其資料類型;例如,電子郵件資料的子類別為 ContactsContract.CommonDataKinds.Email,而電子郵件資料的自訂 MIME 類型是由常數 Email.CONTENT_ITEM_TYPE 定義。

使用 ContactsContract.Data 表格搜尋。這個資料表會定義或沿用投影、選取子句和排序順序所需的所有常數。

定義投影

如要定義投影,請選擇 ContactsContract.Data 中定義的一或多個資料欄,或是從該投影沿用的類別。聯絡人供應程式會在 ContactsContract.Data 與其他資料表之間以隱含方式彙整,再傳回資料列。例如:

Kotlin

@SuppressLint("InlinedApi")
private val PROJECTION: Array<out String> = arrayOf(
        /*
         * The detail data row ID. To make a ListView work,
         * this column is required.
         */
        ContactsContract.Data._ID,
        // The primary display name
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
            ContactsContract.Data.DISPLAY_NAME_PRIMARY
        else
            ContactsContract.Data.DISPLAY_NAME,
        // The contact's _ID, to construct a content URI
        ContactsContract.Data.CONTACT_ID,
        // The contact's LOOKUP_KEY, to construct a content URI
        ContactsContract.Data.LOOKUP_KEY
)

Java

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

定義搜尋條件

如要搜尋特定類型的資料中的字串,請從以下內容建構選取子句:

  • 包含搜尋字串的資料欄名稱。該名稱會因資料類型而異,因此請找出與資料類型對應的 ContactsContract.CommonDataKinds 子類別,然後從該子類別中選擇資料欄名稱。舉例來說,如要搜尋電子郵件地址,請使用 Email.ADDRESS 欄。
  • 搜尋字串本身,代表選取子句中的「?」字元。
  • 包含自訂 MIME 類型值的資料欄名稱。此名稱一律為 Data.MIMETYPE
  • 資料類型的自訂 MIME 類型值。如前所述,這是 ContactsContract.CommonDataKinds 子類別中的常數 CONTENT_ITEM_TYPE。舉例來說,電子郵件資料的 MIME 類型值為 Email.CONTENT_ITEM_TYPE。將「'」(單引號) 字元串連為常數的開頭和結尾,以半形引號括住值;否則,供應器會將該值解讀為變數名稱,而非字串值。這個值不需要預留位置,因為使用的是常數,而非使用者提供的值。

例如:

Kotlin

/*
 * Constructs search criteria from the search string
 * and email MIME type
 */
private val SELECTION: String =
        /*
         * 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.
         */
        "${ContactsContract.Data.MIMETYPE } = '${Email.CONTENT_ITEM_TYPE}'"

Java

    /*
     * 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.
             */
            ContactsContract.Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";

接著,定義變數來包含選取項目引數:

Kotlin

    private var searchString: String? = null
    private val selectionArgs: Array<String> = arrayOf("")

Java

    String searchString;
    String[] selectionArgs = { "" };

導入 onCreateLoader()

您已指定所需資料及取得資料的方式,請在 onCreateLoader() 的實作中定義查詢。使用投影、選取文字運算式和選取陣列做為引數,從這個方法傳回新的 CursorLoader。如果是內容 URI,請使用 Data.CONTENT_URI。例如:

Kotlin

    override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
        // OPTIONAL: Makes search string into pattern
        searchString = "%$mSearchString%"

        searchString?.also {
            // Puts the search string into the selection criteria
            selectionArgs[0] = it
        }
        // Starts the query
        return activity?.let {
            CursorLoader(
                    it,
                    ContactsContract.Data.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    selectionArgs,
                    null
            )
        } ?: throw IllegalStateException()
    }

Java

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

這些程式碼片段是根據特定類型的詳細資料進行簡易反向查詢的基礎。如果應用程式著重於特定類型的資料 (例如電子郵件),而且您想讓使用者取得與這類資料相關聯的名稱,就適合使用這種技巧。

依任何類型的資料比對聯絡人

根據任何類型的資料擷取聯絡人,如果有任何資料與搜尋字串相符 (包括姓名、電子郵件地址、郵寄地址、電話號碼等),則會傳回聯絡人。進而找到多種搜尋結果。例如,如果搜尋字串是「Doe」,則搜尋任何資料類型時,系統會傳回「John Doe」聯絡人;此外,也會傳回住在「Doe Street」上的聯絡人。

如要實作這種擷取類型,請先實作下列程式碼,如前述部分所示:

  • 要求讀取提供者的權限。
  • 定義 ListView 和項目版面配置。
  • 定義顯示聯絡人清單的片段。
  • 定義全域變數。
  • 初始化 Fragment。
  • 設定 ListView 的 CursorAdapter。
  • 設定所選聯絡人監聽器。
  • 定義投影。
  • 定義遊標欄索引的常數。

    在這個類型的擷取作業中,您使用的表格和「依名稱比對聯絡人名稱並列出結果」一節中使用的相同。並使用相同的資料欄索引。

  • 定義 onItemClick() 方法。
  • 初始化載入器。
  • 實作 onLoadFinished() 和 onLoaderReset()。

下列步驟說明您需要的其他程式碼,用於比對搜尋字串和任何類型的資料,並顯示結果。

移除選取條件

請勿定義 SELECTION 常數或 mSelectionArgs 變數。這些函式不會用於這類擷取。

導入 onCreateLoader()

實作 onCreateLoader() 方法,傳回新的 CursorLoader。搜尋字串會由聯絡人供應程式自動執行,因此您不需要將搜尋字串轉換為模式。使用 Contacts.CONTENT_FILTER_URI 做為基準 URI,然後呼叫 Uri.withAppendedPath() 將搜尋字串附加至該字串。使用這個 URI 會自動觸發搜尋任何資料類型,如以下範例所示:

Kotlin

    override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
        /*
         * Appends the search string to the base URI. Always
         * encode search strings to ensure they're in proper
         * format.
         */
        val contentUri: Uri = Uri.withAppendedPath(
                ContactsContract.Contacts.CONTENT_FILTER_URI,
                Uri.encode(searchString)
        )
        // Starts the query
        return activity?.let {
            CursorLoader(
                    it,
                    contentUri,
                    PROJECTION2,
                    null,
                    null,
                    null
            )
        } ?: throw IllegalStateException()
    }

Java

    @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(searchString));
        // Starts the query
        return new CursorLoader(
                getActivity(),
                contentUri,
                PROJECTION,
                null,
                null,
                null
        );
    }

這些程式碼片段是應用程式的基礎,可廣泛搜尋 Contact Provider。如果應用程式要實作與「使用者」應用程式聯絡人清單畫面類似的功能,就很適合使用這項技術。