En esta lección, se muestra cómo recuperar datos detallados de un contacto, como direcciones de correo electrónico, números de teléfono números, etcétera. Estos son los detalles que buscan los usuarios cuando acceden a un contacto. Puedes proporcionarles todos los detalles de un contacto o solo mostrar los detalles de un tipo determinado. como direcciones de correo electrónico.
En los pasos que se describen en esta lección, se asume que ya tienes una fila de ContactsContract.Contacts
para un contacto que el usuario eligió.
La lección Cómo recuperar nombres de contactos muestra las siguientes instrucciones:
recuperar una lista de contactos.
Cómo obtener todos los detalles de un contacto
Para obtener todos los detalles de un contacto, busca en la tabla ContactsContract.Data
las filas que contengan la LOOKUP_KEY
del contacto. Esta columna está disponible en
la tabla ContactsContract.Data
, ya que los Contactos
El proveedor realiza una unión implícita entre ContactsContract.Contacts
y la tabla ContactsContract.Data
. La columna LOOKUP_KEY
se describe con más detalle en la lección Cómo obtener nombres de contactos.
Nota: Obtener todos los detalles de un contacto reduce el rendimiento de la
dispositivo, ya que necesita recuperar todas las columnas de la
Tabla ContactsContract.Data
. Considera el impacto en el rendimiento antes
si usas esta técnica.
Solicita permisos
Si quieres leer datos del Proveedor de contactos, tu app debe tener el permiso READ_CONTACTS
.
Para solicitar este permiso, agrega el siguiente elemento secundario de
<manifest>
a tu archivo de manifiesto:
<uses-permission android:name="android.permission.READ_CONTACTS" />
Configura una proyección
En función del tipo de datos que contiene una fila, se usan pocas o varias columnas. Además,
los datos están en columnas diferentes
según el tipo de datos.
Para asegurarte de obtener todas las columnas posibles para todos los tipos de datos disponibles, debes agregar todos los nombres de columnas en tu proyección. Recuperar siempre
Data._ID
si vinculas el resultado
Cursor
por ListView
De lo contrario, la vinculación
no funcionará. Recuperar también Data.MIMETYPE
para que puedas identificar el tipo de datos de cada fila que recuperes. Por ejemplo:
Kotlin
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 )
Java
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 };
Esta proyección obtiene todas las columnas de una fila en la tabla ContactsContract.Data
y usa los nombres de columna definidos en la clase ContactsContract.Data
.
De manera opcional, también puedes usar otras constantes de columnas que se definieron en la clase ContactsContract.Data
. Sin embargo, ten en cuenta que las columnas SYNC1
a SYNC4
son para los adaptadores de sincronización, por lo que sus datos no son útiles.
Cómo definir los criterios de selección
Define una constante para tu cláusula de selección, un array para conservar argumentos de selección y una variable para mantener el valor de selección. Usa
la columna Contacts.LOOKUP_KEY
a
encontrar el contacto. Por ejemplo:
Kotlin
// 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
Java
// 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
Usa "?" como marcador de posición en la expresión de texto de selección garantiza que la búsqueda se genera a través de la vinculación en lugar de la compilación de SQL. Este enfoque elimina de inyección de SQL maliciosa.
Cómo definir el orden de clasificación
Define el orden de clasificación que quieras para el Cursor
resultante. Para mantener juntas todas las filas de un tipo específico de datos, ordénalas por Data.MIMETYPE
. Este argumento de consulta
agrupa todas las filas de correo electrónico, todas las filas de teléfonos y así sucesivamente. Por ejemplo:
Kotlin
/* * Defines a string that specifies a sort order of MIME type */ private const val SORT_ORDER = ContactsContract.Data.MIMETYPE
Java
/* * Defines a string that specifies a sort order of MIME type */ private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;
Nota: Algunos tipos de datos no usan un subtipo, por lo que no podrás ordenarlos por esta categoría.
En su lugar, debes iterar a través del Cursor
que se muestra,
determinar el tipo de datos de la fila actual y almacenar datos para las filas que usan un subtipo Cuando termines de leer el cursor, podrás ordenar cada tipo de datos por subtipo y mostrar los resultados.
Inicializa el cargador
Siempre debes realizar las recuperaciones del Proveedor de contactos (y todos los otros proveedores de contenido) en un subproceso en segundo plano. Usa el framework de Loader definido por la clase LoaderManager
y la interfaz LoaderManager.LoaderCallbacks
para realizar recuperaciones en segundo plano.
Cuando esté todo listo para recuperar las filas, inicializa el framework del cargador. Para ello, haz lo siguiente:
llamando a initLoader()
. Pasa un identificador de número entero al método; este identificador se pasa a los métodos LoaderManager.LoaderCallbacks
. El identificador te ayuda a usar varios cargadores en una app, ya que te permite diferenciarlos.
En el siguiente fragmento, se muestra cómo inicializar el marco de trabajo del cargador:
Kotlin
// 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)
Java
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);
Cómo implementar onCreateLoader()
Implementa el método onCreateLoader()
, al que llama el framework del cargador inmediatamente después de que llamas.
initLoader()
Devuelve un
CursorLoader
de este método. Como estás buscando
la tabla ContactsContract.Data
, usa la constante
Data.CONTENT_URI
como el URI de contenido.
Por ejemplo:
Kotlin
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 }
Java
@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 ); }
Cómo implementar onLoadFinished() y onLoaderReset()
Implementa el
onLoadFinished()
. El framework del cargador llama
onLoadFinished()
Cuando el Proveedor de contactos devuelve los resultados de la consulta Por ejemplo:
Kotlin
override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) { when(loader.id) { DETAILS_QUERY_ID -> { /* * Process the resulting Cursor here. */ } ... } }
Java
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { switch (loader.getId()) { case DETAILS_QUERY_ID: /* * Process the resulting Cursor here. */ } break; ... } }
El método onLoaderReset()
se invoca cuando el framework del cargador detecta que los datos que respaldan el resultado
Cursor
cambió. En este punto, quita cualquier referencia existente a Cursor
; para ello, configúrala como nula. Si no lo haces, el framework del cargador no destruirá el Cursor
anterior y se producirá una fuga de memoria. Por ejemplo:
Kotlin
override fun onLoaderReset(loader: Loader<Cursor>) { when (loader.id) { DETAILS_QUERY_ID -> { /* * If you have current references to the Cursor, * remove them here. */ } ... } }
Java
@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; }
Cómo obtener detalles específicos de un contacto
Cuando se recupera un tipo de datos específico de un contacto, como todos los correos electrónicos, se sigue el mismo patrón. que recuperar todos los detalles. Estos son los únicos cambios que debes realizar en el código que aparece en Cómo obtener todos los detalles de un contacto:
- Proyección
-
Modifica tu proyección para obtener las columnas que son específicas del tipo de datos. Además, modifica la proyección para usar las constantes de nombres de columna definidas en la subclase
ContactsContract.CommonDataKinds
que corresponde al tipo de datos. - Selección
-
Modifica el texto de selección para buscar el
MIMETYPE
específico de tu tipo de datos. - Ordenar
-
Como solo seleccionas un único tipo de datos, no debes agrupar el
Cursor
que se muestra porData.MIMETYPE
.
Estas modificaciones se describen en las siguientes secciones.
Cómo definir una proyección
Define las columnas que quieras recuperar con las constantes de nombres de columnas de la subclase
de ContactsContract.CommonDataKinds
para el tipo de datos.
Si planeas vincular tu Cursor
a una ListView
,
asegúrate de recuperar la columna _ID
. Por ejemplo, para obtener los datos de correo electrónico, define la siguiente proyección:
Kotlin
private val PROJECTION: Array<String> = arrayOf( ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL )
Java
private static final String[] PROJECTION = { ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL };
Ten en cuenta que esta proyección usa los nombres de columna definidos en la clase ContactsContract.CommonDataKinds.Email
en lugar de los nombres de columna definidos en la clase ContactsContract.Data
. Cómo usar la configuración
y los nombres de columna
hacen que el código sea más legible.
En la proyección, también puedes usar otras columnas definidas en la subclase ContactsContract.CommonDataKinds
.
Cómo definir criterios de selección
Definir una expresión de texto de búsqueda que recupere filas para un contacto específico
LOOKUP_KEY
y las
Data.MIMETYPE
de los detalles que
quieren. Encierra el valor de MIMETYPE
entre comillas simples mediante la concatenación de un carácter "'
" (comilla simple) al principio y al final de la constante; de lo contrario, el proveedor interpretará la constante como nombre de variable en lugar de valor de cadena. No es necesario que uses un marcador de posición para este valor
con una constante en lugar de un valor
proporcionado por el usuario. Por ejemplo:
Kotlin
/* * 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("")
Java
/* * 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 = { "" };
Cómo definir un orden de clasificación
Define un orden de clasificación para el Cursor
resultante. Como lo que obtienes es un tipo de datos específico, deberás omitir el orden en MIMETYPE
.
En su lugar, si el tipo de datos detallados que buscas incluye un subtipo, ordénalos por subtipo.
Por ejemplo, en el caso de los datos de correo electrónico, puedes ordenar
Email.TYPE
:
Kotlin
private const val SORT_ORDER: String = "${Email.TYPE} ASC"
Java
private static final String SORT_ORDER = Email.TYPE + " ASC ";