Получить список контактов

В этом уроке показано, как получить список контактов, данные которых полностью или частично соответствуют строке поиска, используя следующие методы:

Сопоставить имена контактов
Получите список контактов, сопоставив строку поиска со всеми или частью данных имени контакта. Поставщик контактов допускает несколько экземпляров одного и того же имени, поэтому этот метод может возвращать список совпадений.
Сопоставление определенного типа данных, например номера телефона.
Получите список контактов, сопоставив строку поиска с конкретным типом подробных данных, например адресом электронной почты. Например, этот метод позволяет вам составить список всех контактов, адрес электронной почты которых соответствует строке поиска.
Сопоставление любого типа данных
Получите список контактов, сопоставив строку поиска с любым типом подробных данных, включая имя, номер телефона, почтовый адрес, адрес электронной почты и т. д. Например, этот метод позволяет вам принимать любой тип данных для строки поиска, а затем перечислять контакты, для которых данные соответствуют строке.

Примечание. Во всех примерах этого урока используется 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
        );
    }

Эти фрагменты кода составляют основу приложения, которое выполняет широкий поиск по поставщику контактов. Этот метод полезен для приложений, которые хотят реализовать функциональность, аналогичную экрану списка контактов приложения «Люди».