本課程將說明如何使用下列技術,擷取資料與搜尋字串完全或部分相符的聯絡人清單:
- 比對聯絡人姓名
- 將搜尋字串與聯絡人名稱資料進行比對,以擷取聯絡人清單。聯絡人供應程式允許多個相同名稱的例項,因此這項技術可以傳回比對結果清單。
- 比對特定類型的資料,例如電話號碼
- 比對搜尋字串和特定類型的詳細資料資料 (例如電子郵件地址),擷取聯絡人清單。舉例來說,這項技術可讓您列出電子郵件地址與搜尋字串相符的所有聯絡人。
- 比對任何類型的資料
- 將搜尋字串與任何類型的詳細資料 (包括姓名、電話號碼、街道地址、電子郵件地址等) 進行比對,以擷取聯絡人名單。舉例來說,您可以使用這項技術接受搜尋字串的任何類型資料,然後列出資料與該字串相符的聯絡人。
注意:本課程的所有範例都會使用 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
顯示使用者從清單中選擇的聯絡人詳細資料。使用這個方法時,您可以將本課程介紹的其中一項技術與
擷取聯絡人的詳細資料課程結合使用。
如要瞭解如何使用 Activity
中的一或多個 Fragment
物件,請參閱訓練類別
使用 Fragment 建構動態 UI。
為協助您針對聯絡人供應程式編寫查詢,Android 架構提供名為 ContactsContract
的合約類別,其中定義了用於存取供應程式的實用常數和方法。使用這個類別時,您不必為內容 URI、資料表名稱或資料欄定義專屬常數。如要使用這個類別,請加入下列陳述式:
import android.provider.ContactsContract
import android.provider.ContactsContract;
由於程式碼會使用 CursorLoader
從提供者擷取資料,因此您必須指定該程式碼實作載入器介面 LoaderManager.LoaderCallbacks
。此外,為了協助偵測使用者從搜尋結果清單中選取的聯絡人,請實作 AdapterView.OnItemClickListener
轉接介面。例如:
... import android.support.v4.app.Fragment import android.support.v4.app.LoaderManager import android.widget.AdapterView ... class ContactsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor>, 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 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
... /* * 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
物件的 UI。例如:
// 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) }
// 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
。例如:
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 } }
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
。例如:
fun onActivityCreated(savedInstanceState:Bundle) { ... // Set the item click listener to be the current fragment. contactsList.onItemClickListener = this ... }
public void onActivityCreated(Bundle savedInstanceState) { ... // Set the item click listener to be the current fragment. contactsList.setOnItemClickListener(this); ... }
由於您已指定目前的 Fragment
為 ListView
的 OnItemClickListener
,因此現在需要實作其所需的 onItemClick()
方法,以便處理點擊事件。我們會在成功的章節中說明。
定義投影
定義常數,該常數包含您要從查詢中傳回的資料欄。ListView
中的每個項目都會顯示聯絡人的顯示名稱,其中包含聯絡人名稱的主要形式。在 Android 3.0 (API 級別 11) 以上版本中,這個欄的名稱為 Contacts.DISPLAY_NAME_PRIMARY
;在先前版本中,其名稱為 Contacts.DISPLAY_NAME
。
SimpleCursorAdapter
繫結程序會使用資料欄 Contacts._ID
。Contacts._ID
和 LOOKUP_KEY
可搭配使用,為使用者選取的聯絡人建構內容 URI。
... @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 )
... @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
資料欄的索引定義常數,因為索引與投影中的資料欄名稱順序相同。例如:
// 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
// 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 插入的可能性。例如:
// 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)
// 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()
方法,為事件監聽器實作相關動作:
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. */ } }
@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()
中執行初始化作業,如以下範例所示:
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)
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 會參照整個資料表,如以下範例所示:
... 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() }
... @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
:
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) { // Put the result Cursor in the adapter for the ListView cursorAdapter?.swapCursor(cursor) }
@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
,導致記憶體流失。例如:
override fun onLoaderReset(loader: Loader<Cursor>) { // Delete the reference to the existing Cursor cursorAdapter?.swapCursor(null) }
@Override public void onLoaderReset(Loader<Cursor> loader) { // Delete the reference to the existing Cursor cursorAdapter.swapCursor(null); }
您現在已擁有應用程式的關鍵部分,可將搜尋字串與聯絡人名稱比對,並在 ListView
中傳回結果。使用者可以按一下聯絡人姓名來選取。這會觸發事件監聽器,讓您進一步處理聯絡人的資料。例如,您可以擷取聯絡人的詳細資料。如要瞭解如何執行這項操作,請繼續閱讀下一課「擷取聯絡人的詳細資料」。
如要進一步瞭解搜尋使用者介面,請參閱 API 指南的「建立搜尋介面」一節。
本課程的其餘部分會示範在聯絡人供應器中尋找聯絡人的其他方式。
依據特定類型的資料比對聯絡人
這項技巧可讓您指定要比對的資料類型。這種特定查詢類型是以名稱擷取,不過也能針對與聯絡人相關的任何類型詳細資料資料執行擷取作業。舉例來說,您可以擷取具有特定郵遞區號的聯絡人;在這種情況下,搜尋字串必須與郵遞區號資料列中儲存的資料相符。
如要實作這類擷取作業,請先實作下列程式碼,如前面各節所述:
- 要求讀取提供者的權限。
- 定義 ListView 和項目版面配置。
- 定義顯示聯絡人清單的 Fragment。
- 定義全域變數。
- 初始化 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
和其他資料表之間進行隱含彙整。例如:
@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 )
@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
。將「'
」(單引號) 字元連結至常數的開頭和結尾,以單引號括住值;否則,提供者會將值解讀為變數名稱,而非字串值。您不需要針對這個值使用預留位置,因為您使用的是常數 (而非使用者提供的值)。
例如:
/* * 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}'"
/* * 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 + "'";
接著,定義變數來包含選取引數:
private var searchString: String? = null private val selectionArgs: Array<String> = arrayOf("")
String searchString; String[] selectionArgs = { "" };
實作 onCreateLoader()
您已指定所需資料和查詢方式,請在 onCreateLoader()
的實作中定義查詢。使用投影、選取文字運算式和選取陣列做為引數,從這個方法傳回新的 CursorLoader
。如為內容 URI,請使用 Data.CONTENT_URI
。例如:
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() }
@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。
- 定義全域變數。
- 初始化 Fragment。
- 為 ListView 設定 CursorAdapter。
- 設定所選聯絡人事件監聽器。
- 定義投影。
-
定義游標欄索引的常數。
針對這類擷取作業,您會使用「依名稱比對聯絡人並列出結果」一節中使用的相同表格。並使用相同的欄索引。
- 定義 onItemClick() 方法。
- 初始化載入器。
- 實作 onLoadFinished() 和 onLoaderReset()。
下列步驟會顯示您需要的額外程式碼,以便將搜尋字串與任何類型的資料配對,並顯示結果。
移除選取條件
請勿定義 SELECTION
常數或 mSelectionArgs
變數。但無法用於這類擷取。
實作 onCreateLoader()
實作 onCreateLoader()
方法,傳回新的 CursorLoader
。您不需要將搜尋字串轉換為模式,因為聯絡資訊提供者會自動執行這項操作。使用 Contacts.CONTENT_FILTER_URI
做為基準 URI,並呼叫 Uri.withAppendedPath()
將搜尋字串附加到其中。使用這個 URI 會自動觸發任何資料類型的搜尋作業,如以下範例所示:
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() }
@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 ); }
這些程式碼片段是應用程式對「聯絡人供應程式」進行廣泛搜尋的基礎, 如果應用程式想要實作與「使用者」應用程式聯絡人清單畫面類似的功能,這個技巧就非常實用。