Questa lezione mostra come recuperare i dati dettagliati di un contatto, ad esempio indirizzi email, numeri di telefono e così via. Si tratta dei dettagli che gli utenti cercano quando recuperano un contatto. Puoi fornire tutti i dettagli di un contatto o mostrare solo i dettagli di un tipo specifico, come gli indirizzi email.
I passaggi di questa lezione presuppongono che tu abbia già
ContactsContract.Contacts
riga per un contatto scelto dall'utente.
La lezione Recupero dei nomi dei contatti mostra come recuperare un elenco di contatti.
Recupera tutti i dettagli di un contatto
Per recuperare tutti i dettagli di un contatto, cerca nella tabella
ContactsContract.Data
le righe che contengono il LOOKUP_KEY
del contatto. Questa colonna è disponibile in:
tabella ContactsContract.Data
, perché i contatti
Il provider crea un join implicito tra ContactsContract.Contacts
e la tabella ContactsContract.Data
. La colonna LOOKUP_KEY
è descritta più in dettaglio nella lezione Recupero dei nomi dei contatti.
Nota: il recupero di tutti i dettagli di un contatto riduce il rendimento di un
dispositivo, in quanto deve recuperare tutte le colonne della tabella
ContactsContract.Data
. Valuta l'impatto sulle prestazioni prima di utilizzare questa tecnica.
Richiedi autorizzazioni
Per leggere dal provider di contatti, la tua app deve disporre dell'autorizzazione READ_CONTACTS
.
Per richiedere questa autorizzazione, aggiungi il seguente elemento secondario di
<manifest>
al file manifest:
<uses-permission android:name="android.permission.READ_CONTACTS" />
Configurare una proiezione
A seconda del tipo di dati contenuti, una riga può utilizzare solo alcune colonne o molte. Inoltre,
i dati si trovano in colonne diverse a seconda del tipo di dati.
Per assicurarti di ottenere tutte le colonne possibili per tutti i tipi di dati possibili, devi aggiungere tutte le colonne
i nomi delle colonne alla tua proiezione. Recupera sempre
Data._ID
se stai vincolando il risultato
Cursor
a ListView
; altrimenti l'associazione
non funzioneranno. Recupera anche Data.MIMETYPE
per identificare il tipo di dati di ogni riga recuperata. Ad esempio:
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 };
Questa proiezione recupera tutte le colonne per una riga nella
ContactsContract.Data
, utilizzando i nomi delle colonne definiti in
la classe ContactsContract.Data
.
Se vuoi, puoi anche utilizzare qualsiasi altra costante di colonna definita o ereditata dalla classe
ContactsContract.Data
. Nota, tuttavia, che le colonne
Da SYNC1
a
I SYNC4
sono pensati per essere utilizzati dalla sincronizzazione
adattatori, quindi i relativi dati non sono utili.
Definisci i criteri di selezione
Definisci una costante per la clausola di selezione, un array per contenere gli argomenti di selezione e un
per contenere il valore di selezione. Utilizza
la colonna Contacts.LOOKUP_KEY
per
trovare il contatto. Ad esempio:
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
L'utilizzo di "?" come segnaposto nell'espressione di testo della selezione garantisce che la ricerca risultante sia generata dalla definizione dei vincoli anziché dalla compilazione SQL. Questo approccio elimina la possibilità di SQL injection dannoso.
Definisci l'ordinamento
Definisci l'ordinamento che preferisci nel Cursor
risultante. A
conservare insieme tutte le righe di un determinato tipo di dati, ordinare per
Data.MIMETYPE
. Questo argomento di query
raggruppa tutte le righe di email, tutte le righe di telefono e così via. Ad esempio:
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: alcuni tipi di dati non utilizzano un sottotipo, pertanto non puoi ordinare in base al sottotipo.
Devi invece eseguire l'iterazione dell'Cursor
restituito,
determinare il tipo di dati della riga corrente e memorizzare i dati per le righe che utilizzano un sottotipo. Al termine della lettura del cursore, puoi ordinare ogni tipo di dati per sottotipo e visualizzare i risultati.
Inizializzare il caricatore
Esegui sempre i recuperi dal provider di contatti (e da tutti gli altri fornitori di contenuti) in un
thread in background. Utilizza il framework Loader definito dalla classe LoaderManager
e dall'interfaccia LoaderManager.LoaderCallbacks
per eseguire ricerche in background.
Quando è tutto pronto per recuperare le righe, inizializza il framework del caricatore chiamando initLoader()
. Passa un
identificatore intero al metodo; questo identificatore viene passato ai metodi
LoaderManager.LoaderCallbacks
. L'identificatore ti aiuta
a utilizzare più caricatori in un'app consentendoti di distinguerli.
Lo snippet seguente mostra come inizializzare il framework del caricatore:
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);
Implementare onCreateLoader()
Implementare il metodo onCreateLoader()
, che viene chiamato dal framework di caricamento subito dopo la chiamata
initLoader()
. Restituisci un
CursorLoader
da questo metodo. Dato che stai cercando
la tabella ContactsContract.Data
, utilizza la costante
Data.CONTENT_URI
come URI dei contenuti.
Ad esempio:
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 ); }
Implementa onLoadFinished() e onLoaderReset()
Implementa il
onLoadFinished()
. Il framework del caricatore
onLoadFinished()
quando il provider di contatti restituisce i risultati della query. Ad esempio:
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; ... } }
Il metodo onLoaderReset()
viene richiamato quando il framework del caricatore rileva che i dati a supporto del risultato
Il campo Cursor
è cambiato. A questo punto, rimuovi eventuali riferimenti esistenti
al Cursor
impostandoli su null. In caso contrario, il caricatore
non distruggerà il vecchio Cursor
e otterrai una memoria
perdita di dati. Ad esempio:
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; }
Recuperare dettagli specifici di un contatto
Il recupero di un tipo di dati specifico per un contatto, ad esempio tutte le email, segue lo stesso pattern recuperando tutti i dettagli. Queste sono le uniche modifiche che devi apportare al codice elencati in Recuperare tutti i dettagli di un contatto:
- Proiezione
-
Modifica la proiezione per recuperare le colonne specifiche
tipo di dati. Modifica anche la proiezione in modo da utilizzare le costanti dei nomi di colonna definite nella
sottoclasse
ContactsContract.CommonDataKinds
corrispondente al tipo di dati. - Selezione
-
Modifica il testo della selezione per cercare il valore
MIMETYPE
specifico per il tuo tipo di dati. - Ordinamento
-
Poiché stai selezionando un solo tipo di dettaglio, non raggruppare il valore
Cursor
restituito perData.MIMETYPE
.
Queste modifiche sono descritte nelle sezioni seguenti.
Definisci una proiezione
Definisci le colonne che vuoi recuperare utilizzando le costanti dei nomi di colonna nella sottoclasse
di ContactsContract.CommonDataKinds
per il tipo di dati.
Se prevedi di associare Cursor
a un ListView
,
assicurati di recuperare la colonna _ID
. Ad esempio, per recuperare i dati dell'email, definisci
la seguente proiezione:
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 };
Tieni presente che questa proiezione utilizza i nomi delle colonne definiti nella classe
ContactsContract.CommonDataKinds.Email
, anziché i nomi delle colonne
definita nella classe ContactsContract.Data
. Utilizzando l'email
i nomi delle colonne rendono più leggibile il codice.
Nella proiezione, puoi anche utilizzare qualsiasi altra colonna definita nel
ContactsContract.CommonDataKinds
sottoclasse.
Definisci i criteri di selezione
Definisci un'espressione di testo di ricerca che recuperi le righe per il
LOOKUP_KEY
e
Data.MIMETYPE
dei dettagli che
desiderato. Racchiudi il valore MIMETYPE
tra virgolette singole concatenando un carattere "'
" (virgolette singole) all'inizio e alla fine della costante. In caso contrario, il provider interpreta la costante come nome di una variabile anziché come valore di stringa. Non è necessario utilizzare un segnaposto per questo valore, perché stai utilizzando una costante anziché un valore fornito dall'utente. Ad esempio:
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 = { "" };
Definisci un ordinamento
Definisci un ordinamento per i valori Cursor
restituiti. Poiché stai recuperando un
tipo di dati specifico, ometti l'ordinamento su MIMETYPE
.
Invece, se il tipo di dati dettagliati che stai cercando include un sottotipo, ordinali in base a questi dati.
Ad esempio, per i dati email puoi ordinare in base a
Email.TYPE
:
Kotlin
private const val SORT_ORDER: String = "${Email.TYPE} ASC"
Java
private static final String SORT_ORDER = Email.TYPE + " ASC ";