本课程向您展示如何使用以下技术检索其数据与搜索字符串全部或部分匹配的联系人列表
- 匹配联系人姓名
- 通过将搜索字符串与联系人姓名数据的全部或部分进行匹配来检索联系人列表。联系人提供程序允许使用同一个姓名的多个实例,因此此技术可以返回匹配列表。
- 匹配特定类型的数据,例如手机号码
- 通过将搜索字符串与特定类型的详细数据(例如电子邮件地址)进行匹配来检索联系人列表。例如,此技术可以列出所有电子邮件地址与搜索字符串匹配的联系人。
- 匹配任何类型的数据
- 通过将搜索字符串与任何类型的详细数据(包括姓名、电话号码、街道地址、电子邮件地址等)进行匹配来检索联系人列表。例如,此技术允许您接受任何类型的数据作为搜索字符串,然后列出与该字符串匹配数据的联系人。
注意:本课程中的所有示例都使用 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
的一行。例如,您可以创建主布局文件 res/layout/contacts_list_view.xml
并包含以下 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
。
定义列表项布局文件 contacts_list_item.xml
并包含以下 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 构建动态界面。
为了帮助您编写针对联系人提供程序的查询,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
对象的 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
设置将搜索结果绑定到 ListView
的 SimpleCursorAdapter
。要获取显示联系人的 ListView
对象,您需要使用 Fragment
的父 activity 调用 Activity.findViewById()
。调用 setAdapter()
时,使用父 activity 的 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); ... }
由于您指定当前的 Fragment
是 ListView
的 OnItemClickListener
,您现在需要实现其必需的方法 onItemClick()
,该方法处理点击事件。后续部分将对此进行说明。
定义投影
定义一个常量,其中包含您要从查询中返回的列。ListView
中的每个项目都显示联系人的显示名称,其中包含联系人姓名的主要形式。在 Android 3.0(API 版本 11)及更高版本中,此列的名称是 Contacts.DISPLAY_NAME_PRIMARY
;在此之前的版本中,其名称是 Contacts.DISPLAY_NAME
。
列 Contacts._ID
由 SimpleCursorAdapter
绑定过程使用。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
中的索引。您可以为 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。
- 定义全局变量。
- 初始化 Fragment。
- 为 ListView 设置 CursorAdapter。
- 设置选定的联系人监听器。
- 定义 Cursor 列索引的常量。
尽管您从不同的表检索数据,但投影中列的顺序相同,因此您可以对 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。
- 定义全局变量。
- 初始化 Fragment。
- 为 ListView 设置 CursorAdapter。
- 设置选定的联系人监听器。
- 定义投影。
- 定义 Cursor 列索引的常量。
对于此类检索,您使用的表与 按姓名匹配联系人并列出结果 部分使用的表相同。使用相同的列索引。
- 定义 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 ); }
这些代码片段是一个应用的基础,该应用对联系人提供程序执行广泛搜索。此技术适用于想要实现类似于“联系人”应用的联系人列表屏幕的应用功能。