В этом уроке показано, как получить подробные данные о контакте, такие как адреса электронной почты, номера телефонов и т. д. Это детали, которые пользователи ищут при получении контакта. Вы можете предоставить им все сведения о контакте или отобразить только сведения определенного типа, например адреса электронной почты.
В шагах этого урока предполагается, что у вас уже есть строка ContactsContract.Contacts
для контакта, выбранного пользователем. На уроке «Получение имен контактов» показано, как получить список контактов.
Получить всю информацию о контакте
Чтобы получить все сведения о контакте, найдите в таблице ContactsContract.Data
все строки, содержащие LOOKUP_KEY
контакта. Этот столбец доступен в таблице ContactsContract.Data
, поскольку поставщик контактов выполняет неявное соединение между таблицей ContactsContract.Contacts
и таблицей ContactsContract.Data
. Более подробно столбец LOOKUP_KEY
описан в уроке «Получение имен контактов» .
Примечание. Получение всех сведений о контакте снижает производительность устройства, поскольку ему необходимо получить все столбцы в таблице ContactsContract.Data
. Прежде чем использовать этот метод, оцените влияние на производительность.
Запросить разрешения
Для чтения данных от поставщика контактов ваше приложение должно иметь разрешение READ_CONTACTS
. Чтобы запросить это разрешение, добавьте следующий дочерний элемент <manifest>
в файл манифеста:
<uses-permission android:name="android.permission.READ_CONTACTS" />
Настройка проекции
В зависимости от типа данных, содержащихся в строке, она может использовать только несколько столбцов или много. Кроме того, данные находятся в разных столбцах в зависимости от типа данных. Чтобы гарантировать получение всех возможных столбцов для всех возможных типов данных, вам необходимо добавить в проекцию все имена столбцов. Всегда извлекайте Data._ID
, если вы привязываете результат Cursor
к ListView
; в противном случае привязка не будет работать. Также извлеките Data.MIMETYPE
, чтобы можно было определить тип данных каждой получаемой строки. Например:
Котлин
private val PROJECTION: Array<out String> = arrayOf( ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA3, ContactsContract.Data.DATA4, ContactsContract.Data.DATA5, ContactsContract.Data.DATA6, ContactsContract.Data.DATA7, ContactsContract.Data.DATA8, ContactsContract.Data.DATA9, ContactsContract.Data.DATA10, ContactsContract.Data.DATA11, ContactsContract.Data.DATA12, ContactsContract.Data.DATA13, ContactsContract.Data.DATA14, ContactsContract.Data.DATA15 )
Ява
private static final String[] PROJECTION = { ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA3, ContactsContract.Data.DATA4, ContactsContract.Data.DATA5, ContactsContract.Data.DATA6, ContactsContract.Data.DATA7, ContactsContract.Data.DATA8, ContactsContract.Data.DATA9, ContactsContract.Data.DATA10, ContactsContract.Data.DATA11, ContactsContract.Data.DATA12, ContactsContract.Data.DATA13, ContactsContract.Data.DATA14, ContactsContract.Data.DATA15 };
Эта проекция извлекает все столбцы строки в таблице ContactsContract.Data
, используя имена столбцов, определенные в классе ContactsContract.Data
.
При желании вы также можете использовать любые другие константы столбцов, определенные в классе ContactsContract.Data
или унаследованные им. Однако обратите внимание, что столбцы SYNC1
до SYNC4
предназначены для использования адаптерами синхронизации, поэтому их данные бесполезны.
Определите критерии выбора
Определите константу для вашего предложения выбора, массив для хранения аргументов выбора и переменную для хранения значения выбора. Используйте столбец Contacts.LOOKUP_KEY
чтобы найти контакт. Например:
Котлин
// Defines the selection clause private const val SELECTION: String = "${ContactsContract.Data.LOOKUP_KEY} = ?" ... // Defines the array to hold the search criteria private val selectionArgs: Array<String> = arrayOf("") /* * Defines a variable to contain the selection value. Once you * have the Cursor from the Contacts table, and you've selected * the desired row, move the row's LOOKUP_KEY value into this * variable. */ private var lookupKey: String? = null
Ява
// Defines the selection clause private static final String SELECTION = Data.LOOKUP_KEY + " = ?"; // Defines the array to hold the search criteria private String[] selectionArgs = { "" }; /* * Defines a variable to contain the selection value. Once you * have the Cursor from the Contacts table, and you've selected * the desired row, move the row's LOOKUP_KEY value into this * variable. */ private lateinit var lookupKey: String
С использованием "?" в качестве заполнителя в текстовом выражении выбора гарантирует, что результирующий поиск будет создан путем привязки, а не компиляции SQL. Такой подход исключает возможность вредоносного внедрения SQL.
Определить порядок сортировки
Определите желаемый порядок сортировки в результирующем Cursor
. Чтобы сохранить все строки для определенного типа данных вместе, выполните сортировку по Data.MIMETYPE
. Этот аргумент запроса группирует все строки электронной почты, все строки телефонов и т. д. Например:
Котлин
/* * Defines a string that specifies a sort order of MIME type */ private const val SORT_ORDER = ContactsContract.Data.MIMETYPE
Ява
/* * Defines a string that specifies a sort order of MIME type */ private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;
Примечание. Некоторые типы данных не используют подтип, поэтому сортировку по подтипу невозможно выполнить. Вместо этого вам придется перебрать возвращенный Cursor
, определить тип данных текущей строки и сохранить данные для строк, которые используют подтип. Когда вы закончите чтение курсора, вы сможете отсортировать каждый тип данных по подтипу и отобразить результаты.
Инициализируйте загрузчик
Всегда выполняйте получение данных от поставщика контактов (и всех других поставщиков контента) в фоновом потоке. Используйте платформу Loader, определенную классом LoaderManager
и интерфейсом LoaderManager.LoaderCallbacks
для фонового извлечения данных.
Когда вы будете готовы получить строки, инициализируйте платформу загрузчика, вызвав initLoader()
. Передайте методу целочисленный идентификатор; этот идентификатор передается методам LoaderManager.LoaderCallbacks
. Идентификатор помогает использовать несколько загрузчиков в приложении, позволяя различать их.
В следующем фрагменте показано, как инициализировать платформу загрузчика:
Котлин
// Defines a constant that identifies the loader private const val DETAILS_QUERY_ID: Int = 0 class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> { ... override fun onCreate(savedInstanceState: Bundle?) { ... // Initializes the loader framework loaderManager.initLoader(DETAILS_QUERY_ID, null, this)
Ява
public class DetailsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { ... // Defines a constant that identifies the loader static int DETAILS_QUERY_ID = 0; ... @Override public void onCreate(Bundle savedInstanceState) { ... // Initializes the loader framework getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
Реализовать onCreateLoader()
Реализуйте метод onCreateLoader()
, который вызывается платформой загрузчика сразу после вызова initLoader()
. Верните CursorLoader
из этого метода. Поскольку вы ищете в таблице ContactsContract.Data
, используйте константу Data.CONTENT_URI
в качестве URI контента. Например:
Котлин
override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> { // Choose the proper action mLoader = when(loaderId) { DETAILS_QUERY_ID -> { // Assigns the selection parameter selectionArgs[0] = lookupKey // Starts the query activity?.let { CursorLoader( it, ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, SORT_ORDER ) } } ... } return mLoader }
Ява
@Override public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { // Choose the proper action switch (loaderId) { case DETAILS_QUERY_ID: // Assigns the selection parameter selectionArgs[0] = lookupKey; // Starts the query CursorLoader mLoader = new CursorLoader( getActivity(), ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, SORT_ORDER ); }
Реализуйте onLoadFinished() и onLoaderReset().
Реализуйте метод onLoadFinished()
. Платформа загрузчика вызывает onLoadFinished()
, когда поставщик контактов возвращает результаты запроса. Например:
Котлин
override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) { when(loader.id) { DETAILS_QUERY_ID -> { /* * Process the resulting Cursor here. */ } ... } }
Ява
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { switch (loader.getId()) { case DETAILS_QUERY_ID: /* * Process the resulting Cursor here. */ } break; ... } }
Метод onLoaderReset()
вызывается, когда платформа загрузчика обнаруживает, что данные, подтверждающие результат Cursor
изменились. На этом этапе удалите все существующие ссылки на Cursor
, установив для них значение null. Если вы этого не сделаете, платформа загрузчика не уничтожит старый Cursor
, и вы получите утечку памяти. Например:
Котлин
override fun onLoaderReset(loader: Loader<Cursor>) { when (loader.id) { DETAILS_QUERY_ID -> { /* * If you have current references to the Cursor, * remove them here. */ } ... } }
Ява
@Override public void onLoaderReset(Loader<Cursor> loader) { switch (loader.getId()) { case DETAILS_QUERY_ID: /* * If you have current references to the Cursor, * remove them here. */ } break; }
Получение конкретных сведений о контакте
Получение определенного типа данных для контакта, например всех электронных писем, происходит по той же схеме, что и получение всех сведений. Это единственные изменения, которые вам нужно внести в код, указанный в разделе Получение всех сведений о контакте :
- Проекция
- Измените свою проекцию, чтобы получить столбцы, относящиеся к типу данных. Также измените проекцию, чтобы использовать константы имен столбцов, определенные в подклассе
ContactsContract.CommonDataKinds
соответствующие типу данных. - Выбор
- Измените текст выделения, чтобы найти значение
MIMETYPE
, соответствующее вашему типу данных. - Порядок сортировки
- Поскольку вы выбираете только один тип детализации, не группируйте возвращаемый
Cursor
поData.MIMETYPE
.
Эти модификации описаны в следующих разделах.
Определить проекцию
Определите столбцы, которые вы хотите получить, используя константы имен столбцов в подклассе ContactsContract.CommonDataKinds
для типа данных. Если вы планируете привязать Cursor
к ListView
, обязательно получите столбец _ID
. Например, чтобы получить данные электронной почты, определите следующую проекцию:
Котлин
private val PROJECTION: Array<String> = arrayOf( ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL )
Ява
private static final String[] PROJECTION = { ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL };
Обратите внимание, что в этой проекции используются имена столбцов, определенные в классе ContactsContract.CommonDataKinds.Email
, вместо имен столбцов, определенных в классе ContactsContract.Data
. Использование имен столбцов, специфичных для электронной почты, делает код более читабельным.
В проекции вы также можете использовать любые другие столбцы, определенные в подклассе ContactsContract.CommonDataKinds
.
Определить критерии выбора
Определите текстовое выражение поиска, которое извлекает строки для LOOKUP_KEY
определенного контакта и Data.MIMETYPE
нужных вам сведений. Заключите значение MIMETYPE
в одинарные кавычки, объединив символ " '
" (одинарные кавычки) в начало и конец константы; в противном случае поставщик интерпретирует константу как имя переменной, а не как строковое значение. Вам не нужно использовать заполнитель для этого значения, поскольку вы используете константу, а не значение, введенное пользователем. Например:
Котлин
/* * Defines the selection clause. Search for a lookup key * and the Email MIME type */ private const val SELECTION = "${ContactsContract.Data.LOOKUP_KEY} = ? AND " + "${ContactsContract.Data.MIMETYPE} = '${Email.CONTENT_ITEM_TYPE}'" ... // Defines the array to hold the search criteria private val selectionArgs: Array<String> = arrayOf("")
Ява
/* * Defines the selection clause. Search for a lookup key * and the Email MIME type */ private static final String SELECTION = Data.LOOKUP_KEY + " = ?" + " AND " + Data.MIMETYPE + " = " + "'" + Email.CONTENT_ITEM_TYPE + "'"; // Defines the array to hold the search criteria private String[] selectionArgs = { "" };
Определить порядок сортировки
Определите порядок сортировки возвращаемого Cursor
. Поскольку вы получаете определенный тип данных, опустите сортировку MIMETYPE
. Вместо этого, если тип искомых подробных данных включает подтип, выполните сортировку по нему. Например, данные электронной почты можно отсортировать по Email.TYPE
:
Котлин
private const val SORT_ORDER: String = "${Email.TYPE} ASC"
Ява
private static final String SORT_ORDER = Email.TYPE + " ASC ";