擷取聯絡人名單

本課程將說明如何擷取資料與部分或部分聯絡人相符的聯絡人名單 搜尋字串,並採用以下技術:

比對聯絡人名稱
將搜尋字串與聯絡人名稱資料的全部或部分比對,擷取聯絡人清單。聯絡人提供者允許多個同名例項,因此這項技巧可傳回符合條件的清單。
比對特定類型的資料,例如電話號碼
將搜尋字串對應至特定類型的詳細資料,從而擷取聯絡人清單 像是電子郵件地址等資料舉例來說,這項技巧可用來列出 其電子郵件地址與搜尋字串相符的聯絡人。
符合任何類型的資料
將搜尋字串與任何類型的詳細資料 (包括姓名、電話號碼、街道地址、電子郵件地址等) 比對,擷取聯絡人清單。例如: 這項技巧可讓您接受任何類型的資料做為搜尋字串,然後將 與字串相符的聯絡人。

注意:本課程的所有範例都會使用 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 的程式碼,以便顯示聯絡人清單。

定義顯示聯絡人清單的 Fragment

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

如要瞭解如何從Fragment Activity,讀取訓練課程 使用 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 Lint 警告: Android Studio。如要關閉這項警示,請新增註解 在 FROM_COLUMNS 定義之前 @SuppressLint("InlinedApi")

初始化 Fragment

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

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

設定 SimpleCursorAdapter,將搜尋結果繫結至 ListView。如要取得顯示聯絡人的 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);
    }

設定所選聯絡人監聽器

顯示搜尋結果時,您通常會希望讓使用者選取單一聯絡人,以便進一步處理。舉例來說,使用者點選聯絡人時 在地圖上顯示聯絡人的地址。為了提供這項功能,您首先將目前的 Fragment 定義為點擊事件監聽器,方法是指定該類別實作 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);
        ...
    }

由於您指定目前的 FragmentOnItemClickListenerListView,您現在需要實作其必要方法 onItemClick(), 處理點擊事件我們會在成功的章節中說明。

定義投影

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

SimpleCursorAdapter 繫結程序會使用資料欄 Contacts._ID。「Contacts._ID」和 搭配使用 LOOKUP_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() 中設定搜尋字串模式。如要將字串設為模式,請插入「%」(百分比) 字元,代表零個或多個字元的序列,或插入「_」(底線) 字元,代表單一字元,或同時插入這兩種字元。舉例來說,「%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() 方法。刪除 SimpleCursorAdapter 個現有參照 Cursor。如果您沒有這樣做,載入器架構就不會回收 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。
  • 設定所選聯絡人監聽器。
  • 定義遊標資料欄索引的常數。

    雖然您是從不同的資料表擷取資料,但 投影相同,因此您可以對遊標使用相同的索引。

  • 定義 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 類型值。如前所述,這是常數 CONTENT_ITEM_TYPEContactsContract.CommonDataKinds 子類別。舉例來說,電子郵件資料的 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
        );
    }

這些程式碼片段是應用程式廣泛搜尋聯絡人供應器的基礎。如果應用程式要實作類似 聯絡人應用程式的聯絡人清單畫面。