В этом уроке показано, как получить список контактов, данные которых полностью или частично соответствуют строке поиска, используя следующие методы:
- Сопоставить имена контактов
- Получите список контактов, сопоставив строку поиска со всеми или частью данных имени контакта. Поставщик контактов допускает несколько экземпляров одного и того же имени, поэтому этот метод может возвращать список совпадений.
- Сопоставление определенного типа данных, например номера телефона.
- Получите список контактов, сопоставив строку поиска с конкретным типом подробных данных, например адресом электронной почты. Например, этот метод позволяет вам составить список всех контактов, адрес электронной почты которых соответствует строке поиска.
- Сопоставление любого типа данных
- Получите список контактов, сопоставив строку поиска с любым типом подробных данных, включая имя, номер телефона, почтовый адрес, адрес электронной почты и т. д. Например, этот метод позволяет вам принимать любой тип данных для строки поиска, а затем перечислять контакты, для которых данные соответствуют строке.
Примечание. Во всех примерах этого урока используется CursorLoader
для получения данных от поставщика контактов. CursorLoader
выполняет свой запрос в потоке, отдельном от потока пользовательского интерфейса. Это гарантирует, что запрос не замедлит время ответа пользовательского интерфейса и не ухудшит взаимодействие с пользователем. Дополнительные сведения см. в учебном курсе Android «Загрузка данных в фоновом режиме» .
Запросить разрешение на чтение поставщика
Для выполнения любого типа поиска поставщика контактов ваше приложение должно иметь разрешение READ_CONTACTS
. Чтобы запросить это, добавьте этот элемент <uses-permission>
в файл манифеста в качестве дочернего элемента <manifest>
:
<uses-permission android:name="android.permission.READ_CONTACTS" />
Сопоставьте контакт по имени и перечислите результаты
Этот метод пытается сопоставить строку поиска с именем контакта или контактов в таблице ContactsContract.Contacts
поставщика контактов. Обычно вы хотите отобразить результаты в ListView
, чтобы позволить пользователю выбирать среди совпадающих контактов.
Определите ListView и макеты элементов
Чтобы отобразить результаты поиска в ListView
, вам нужен основной файл макета, который определяет весь пользовательский интерфейс, включая 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
.
Примечание. В этом уроке не описывается пользовательский интерфейс для получения строки поиска от пользователя, поскольку вам может потребоваться получить строку косвенно. Например, вы можете предоставить пользователю возможность искать контакты, имя которых соответствует строке во входящем текстовом сообщении.
Два написанных вами файла макета определяют пользовательский интерфейс, отображающий ListView
. Следующим шагом будет написание кода, который будет использовать этот пользовательский интерфейс для отображения списка контактов.
Определите фрагмент, который отображает список контактов.
Чтобы отобразить список контактов, начните с определения Fragment
, загружаемого Activity
. Использование Fragment
— более гибкий метод, поскольку вы можете использовать один Fragment
для отображения списка, а второй Fragment
— для отображения сведений о контакте, который пользователь выбирает из списка. Используя этот подход, вы можете объединить один из методов, представленных в этом уроке, с приемом из урока Получение сведений о контакте .
Чтобы узнать, как использовать один или несколько объектов Fragment
из Activity
, прочитайте учебный класс «Создание динамического пользовательского интерфейса с помощью Fragments» .
Чтобы помочь вам писать запросы к поставщику контактов, платформа 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 Lint в Android Studio. Чтобы отключить это предупреждение, добавьте аннотацию @SuppressLint("InlinedApi")
перед определением FROM_COLUMNS
.
Инициализировать фрагмент
Инициализируйте Fragment
. Добавьте пустой общедоступный конструктор, необходимый системе Android, и разверните пользовательский интерфейс объекта Fragment
в методе обратного вызова onCreateView()
. Например:
Котлин
// 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); }
Настройте CursorAdapter для ListView.
Настройте SimpleCursorAdapter
, который привязывает результаты поиска к ListView
. Чтобы получить объект ListView
, отображающий контакты, вам необходимо вызвать Activity.findViewById()
используя родительскую активность Fragment
. Используйте Context
родительской активности при вызове setAdapter()
. Например:
Котлин
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
, как показано в разделе «Определение фрагмента, отображающего список контактов» .
Чтобы продолжить настройку прослушивателя, привяжите его к ListView
, вызвав метод setOnItemClickListener()
в onActivityCreated()
. Например:
Котлин
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
является OnItemClickListener
для ListView
, теперь вам необходимо реализовать его обязательный метод onItemClick()
, который обрабатывает событие щелчка. Это описано в следующем разделе.
Определение проекции
Определите константу, содержащую столбцы, которые вы хотите вернуть из вашего запроса. Каждый элемент в ListView
отображает отображаемое имя контакта, которое содержит основную форму имени контакта. В Android 3.0 (API версии 11) и более поздних версиях имя этого столбца — Contacts.DISPLAY_NAME_PRIMARY
; в предыдущих версиях его имя — Contacts.DISPLAY_NAME
.
Столбец Contacts._ID
используется процессом привязки SimpleCursorAdapter
. 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()
настройте шаблон строки поиска. Чтобы превратить строку в шаблон, вставьте символы «%» (проценты), чтобы представить последовательность из нуля или более символов, или символы «_» (подчеркивание), чтобы представить один символ, или и то, и другое. Например, шаблон «%Джефферсон%» будет соответствовать как «Томасу Джефферсону», так и «Джефферсону Дэвису».
Верните новый 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); }
Метод onLoaderReset()
вызывается, когда платформа загрузчика обнаруживает, что результирующий Cursor
содержит устаревшие данные. Удалите ссылку 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 и макеты элементов.
- Определите фрагмент, который отображает список контактов.
- Определите глобальные переменные.
- Инициализируйте фрагмент.
- Настройте CursorAdapter для ListView.
- Установите выбранного прослушивателя контактов.
- Определите константы для индексов столбцов курсора.
Хотя вы извлекаете данные из другой таблицы, порядок столбцов в проекции тот же, поэтому вы можете использовать те же индексы для курсора.
- Определите метод onItemClick().
- Инициализируйте загрузчик.
- Реализуйте onLoadFinished() и onLoaderReset().
Следующие шаги покажут вам дополнительный код, необходимый для сопоставления строки поиска с конкретным типом подробных данных и отображения результатов.
Выберите тип данных и таблицу
Для поиска определенного типа подробных данных необходимо знать значение пользовательского типа MIME для этого типа данных. Каждый тип данных имеет уникальное значение типа MIME, определенное константой CONTENT_ITEM_TYPE
в подклассе ContactsContract.CommonDataKinds
связанном с типом данных. Подклассы имеют имена, указывающие их тип данных; например, подклассом для данных электронной почты является 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 для типа данных. Как описано ранее, это константа
CONTENT_ITEM_TYPE
в подклассеContactsContract.CommonDataKinds
. Например, значение типа 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 ); }
Эти фрагменты кода являются основой простого обратного поиска на основе определенного типа подробных данных. Это лучший метод, если ваше приложение ориентировано на определенный тип данных, например электронные письма, и вы хотите, чтобы пользователи могли получать имена, связанные с фрагментом данных.
Сопоставление контакта по любому типу данных
Получение контакта на основе любого типа данных возвращает контакты, если какие-либо их данные совпадают со строкой поиска, включая имя, адрес электронной почты, почтовый адрес, номер телефона и т. д. Это приводит к широкому набору результатов поиска. Например, если строкой поиска является «Доу», то поиск по любому типу данных возвращает контакт «Джон Доу»; он также возвращает контакты, которые живут на «Доу-стрит».
Чтобы реализовать этот тип извлечения, сначала реализуйте следующий код, как указано в предыдущих разделах:
- Запросите разрешение на чтение поставщика.
- Определите ListView и макеты элементов.
- Определите фрагмент, который отображает список контактов.
- Определите глобальные переменные.
- Инициализируйте фрагмент.
- Настройте CursorAdapter для ListView.
- Установите выбранного прослушивателя контактов.
- Дайте определение проекции.
- Определите константы для индексов столбцов курсора.
Для этого типа поиска вы используете ту же таблицу, что и в разделе «Сопоставить контакт по имени и перечислить результаты» . Используйте те же индексы столбцов.
- Определите метод 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 ); }
Эти фрагменты кода составляют основу приложения, которое выполняет широкий поиск по поставщику контактов. Этот метод полезен для приложений, которые хотят реализовать функциональность, аналогичную экрану списка контактов приложения «Люди».