Questa lezione mostra come recuperare un elenco di contatti i cui dati corrispondono a tutto o parte di una stringa di ricerca utilizzando le seguenti tecniche:
- Abbinare i nomi dei contatti
- Recupera un elenco di contatti associando la stringa di ricerca a tutto o parte dei dati del nome del contatto. Il provider di contatti consente più istanze con lo stesso nome, quindi questa tecnica può restituire un elenco di corrispondenze.
- Corrispondenza a un tipo specifico di dati, ad esempio un numero di telefono
- Recupera un elenco di contatti associando la stringa di ricerca a un determinato tipo di dati dettagliati, ad esempio un indirizzo email. Ad esempio, questa tecnica ti consente di elencare tutti i contatti il cui indirizzo email corrisponde alla stringa di ricerca.
- Corrisponde a qualsiasi tipo di dati
- Recupera un elenco di contatti associando la stringa di ricerca a qualsiasi tipo di dati dettagliati, tra cui nome, numero di telefono, indirizzo, indirizzo email e così via. Ad esempio, questa tecnica ti consente di accettare qualsiasi tipo di dati per una stringa di ricerca e poi elencare i contatti per i quali i dati corrispondono alla stringa.
Nota:tutti gli esempi in questa lezione utilizzano un
CursorLoader
per recuperare i dati dal Provider di contatti. Un CursorLoader
esegue la propria query su un
thread separato dal thread dell'interfaccia utente. In questo modo, la query non rallenta i tempi di risposta dell'interfaccia utente e non causa un'esperienza utente scadente. Per ulteriori informazioni, consulta il corso di formazione Android
Caricamento di dati in background.
Richiedi l'autorizzazione a leggere il fornitore
Per eseguire qualsiasi tipo di ricerca del fornitore di servizi di contatto, la tua app deve disporre dell'autorizzazione READ_CONTACTS
.
Per richiederlo, aggiungi questo elemento <uses-permission>
al file manifest come elemento secondario di <manifest>
:
<uses-permission android:name="android.permission.READ_CONTACTS" />
Associa un contatto per nome ed elenca i risultati
Questa tecnica tenta di abbinare una stringa di ricerca al nome di un contatto o di contatti nella tabella ContactsContract.Contacts
del provider di contatti. In genere, è consigliabile visualizzare i risultati in un ListView
per consentire all'utente di scegliere tra i contatti corrispondenti.
Definisci i layout di ListView e degli elementi
Per visualizzare i risultati di ricerca in un ListView
, devi avere un file di layout principale
che definisce l'intera UI, incluso ListView
, e un file di layout
di elemento che definisca una riga di ListView
. Ad esempio, puoi creare
il file di layout principale res/layout/contacts_list_view.xml
con
il seguente 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"/>
Questo XML utilizza il widget ListView
integrato di Android
android:id/list
.
Definisci il file di layout dell'articolo contacts_list_item.xml
con il seguente 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"/>
Questo XML utilizza il widget TextView
integrato di Android
android:text1
.
Nota:questa lezione non descrive l'interfaccia utente per ottenere una stringa di ricerca dall'utente, perché potresti voler ottenere la stringa indirettamente. Ad esempio, puoi offrire all'utente la possibilità di cercare contatti il cui nome corrisponde a una stringa in un messaggio di testo in arrivo.
I due file di layout che hai scritto definiscono un'interfaccia utente che mostra un
ListView
. Il passaggio successivo consiste nello scrivere il codice che utilizza questa UI per visualizzare un
elenco di contatti.
Definisci un frammento che mostri l'elenco di contatti
Per visualizzare l'elenco dei contatti, inizia definendo un Fragment
caricato da un Activity
. L'utilizzo di un Fragment
è una tecnica più flessibile, perché puoi utilizzare un Fragment
per visualizzare l'elenco e un secondo Fragment
per visualizzare i dettagli di un contatto scelto dall'utente dall'elenco. Utilizzando questo approccio, puoi combinare una delle tecniche presentate in questa lezione con una della lezione
Recuperare i dettagli di un contatto.
Per scoprire come utilizzare uno o più oggetti Fragment
da un Activity
, leggi il corso di formazione
Creare un'interfaccia utente dinamica con i frammenti.
Per aiutarti a scrivere query sul provider di contatti, il framework Android fornisce una classe contratti chiamata ContactsContract
, che definisce costanti e metodi utili per accedere al provider. Quando utilizzi questa classe, non devi
definire le tue costanti per gli URI dei contenuti, i nomi delle tabelle o le colonne. Per utilizzare questa classe,
includere la seguente dichiarazione:
Kotlin
import android.provider.ContactsContract
Java
import android.provider.ContactsContract;
Poiché il codice utilizza un CursorLoader
per recuperare i dati
dal provider, devi specificare che implementa l'interfaccia del caricatore
LoaderManager.LoaderCallbacks
. Inoltre, per rilevare il contatto selezionato dall'utente dall'elenco dei risultati di ricerca, implementa l'interfaccia dell'adattatoreAdapterView.OnItemClickListener
. Ad esempio:
Kotlin
... import android.support.v4.app.Fragment import android.support.v4.app.LoaderManager import android.widget.AdapterView ... class ContactsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener {
Java
... 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 {
Definisci le variabili globali
Definisci le variabili globali utilizzate in altre parti del codice:
Kotlin
... /* * 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
Java
... /* * 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; ...
Nota: poiché Contacts.DISPLAY_NAME_PRIMARY
richiede Android 3.0 (API versione 11) o versioni successive, se imposti minSdkVersion
dell'app su 10 o versioni precedenti, viene generato un avviso Lint Android in Android Studio. Per disattivare questo avviso, aggiungi l'annotazione
@SuppressLint("InlinedApi")
prima della definizione di FROM_COLUMNS
.
Inizializza il frammento
Inizializza Fragment
. Aggiungi il costruttore pubblico vuoto richiesto dal sistema Android e carica l'interfaccia utente dell'oggetto Fragment
nel metodo di callback onCreateView()
.
Ad esempio:
Kotlin
// 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) }
Java
// 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); }
Configurare CursorAdapter per ListView
Configura il SimpleCursorAdapter
che lega i risultati della ricerca al ListView
. Per ottenere l'oggetto ListView
che mostra i contatti, devi chiamare Activity.findViewById()
utilizzando l'attività principale del
Fragment
. Quando chiami setAdapter()
, utilizza il Context
dell'attività principale.
Ad esempio:
Kotlin
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 } }
Java
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); }
Imposta il listener di contatti selezionato
Quando mostri i risultati di una ricerca, in genere vuoi consentire all'utente di selezionare un singolo contatto per l'ulteriore elaborazione. Ad esempio, quando l'utente fa clic su un contatto, puoi visualizzarne l'indirizzo su una mappa. Per fornire questa funzionalità, hai prima definito il valore corrente
Fragment
come ascoltatore dei clic specificando che la classe
implementa AdapterView.OnItemClickListener
, come mostrato nella sezione
Definire un frammento che mostra l'elenco dei contatti.
Per continuare a configurare il listener, associalo a ListView
chiamando il metodo setOnItemClickListener()
in onActivityCreated()
. Ad esempio:
Kotlin
fun onActivityCreated(savedInstanceState:Bundle) { ... // Set the item click listener to be the current fragment. contactsList.onItemClickListener = this ... }
Java
public void onActivityCreated(Bundle savedInstanceState) { ... // Set the item click listener to be the current fragment. contactsList.setOnItemClickListener(this); ... }
Poiché hai specificato che l'Fragment
corrente è il
OnItemClickListener
per il
ListView
, ora devi implementare il metodo obbligatorio
onItemClick()
, che
gestisce l'evento di clic. Questo aspetto è descritto in una sezione successiva.
Definisci una proiezione
Definisci una costante contenente le colonne da restituire dalla query. Ogni voce in ListView
mostra il nome visualizzato del contatto, che contiene il formato principale del nome del contatto. In Android 3.0 (API versione 11) e successive,
il nome di questa colonna è
Contacts.DISPLAY_NAME_PRIMARY
; nelle versioni precedenti, il nome è
Contacts.DISPLAY_NAME
.
La colonna Contacts._ID
viene utilizzata dalla procedura di associazione SimpleCursorAdapter
.
Contacts._ID
e
LOOKUP_KEY
vengono utilizzati insieme per
creare un URI dei contenuti per il contatto selezionato dall'utente.
Kotlin
... @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 )
Java
... @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 };
Definisci le costanti per gli indici di colonna del cursore
Per recuperare i dati di una singola colonna in un Cursor
, devi avere l'indice della colonna all'interno del Cursor
. Puoi definire le costanti per gli indici delle colonne Cursor
, perché questi ultimi sono uguali all'ordine dei nomi delle colonne nella proiezione. Ad esempio:
Kotlin
// 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
Java
// 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;
Specifica i criteri di selezione
Per specificare i dati che ti interessano, crea una combinazione di espressioni di testo e variabili che indichino al provider le colonne di dati da cercare e i valori da trovare.
Per l'espressione di testo, definisci una costante che elenca le colonne di ricerca. Sebbene questa expression possa contenere anche valori, la prassi preferita è rappresentare i valori con un segnaposto "?". Durante il recupero, il segnaposto viene sostituito con i valori di un array. L'utilizzo di "?" come segnaposto garantisce che la specifica della ricerca venga generata tramite associazione anziché da compilazione SQL. Questa pratica elimina la possibilità di SQL injection dannosi. Ad esempio:
Kotlin
// 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)
Java
// 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 };
Definisci il metodo onItemClick()
In una sezione precedente, hai impostato il listener dei clic sugli elementi per ListView
.
Ora implementa l'azione per l'ascoltatore definendo il metodoAdapterView.OnItemClickListener.onItemClick()
:
Kotlin
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. */ } }
Java
@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. */ }
Inizializza il caricatore
Poiché utilizzi un CursorLoader
per recuperare i dati,
devi inizializzare il thread in background e altre variabili che controllano il recupero asincrono. Esegui l'inizializzazione in
onCreate()
come показано показано nell'esempio seguente:
Kotlin
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)
Java
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);
Implementare onCreateLoader()
Implementa il metodo
onCreateLoader()
,
che viene chiamato dal framework del caricatore immediatamente dopo la chiamata
initLoader()
.
In onCreateLoader()
,
configura lo schema della stringa di ricerca. Per trasformare una stringa in un pattern, inserisci i caratteri "%" (percentuale) per rappresentare una sequenza di zero o più caratteri oppure i caratteri "_" (trattino basso) per rappresentare un singolo carattere oppure entrambi. Ad esempio, il pattern "%Jefferson%"
corrisponderà sia a "Thomas Jefferson" che a "Jefferson Davis".
Restituire un nuovo CursorLoader
dal metodo. Per l'URI
contenuto, utilizza Contacts.CONTENT_URI
.
Questo URI fa riferimento all'intera tabella, come mostrato nell'esempio seguente:
Kotlin
... 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() }
Java
... @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 ); }
Implementazione di onLoadFinished() e onLoaderReset()
Implementa il metodo
onLoadFinished()
. Il framework del caricatore chiama
onLoadFinished()
quando il provider di contatti restituisce i risultati della query. In questo metodo, inserisci il risultato Cursor
in SimpleCursorAdapter
. In questo modo, il campo
ListView
viene aggiornato automaticamente con i risultati di ricerca:
Kotlin
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) { // Put the result Cursor in the adapter for the ListView cursorAdapter?.swapCursor(cursor) }
Java
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { // Put the result Cursor in the adapter for the ListView cursorAdapter.swapCursor(cursor); }
Il metodo onLoaderReset()
viene richiamato quando il framework di caricamento rileva che il risultato Cursor
contiene dati non aggiornati. Elimina il
SimpleCursorAdapter
riferimento al
Cursor
esistente. In caso contrario, il framework del caricatore non riutilizzerà Cursor
, causando una perdita di memoria. Ad esempio:
Kotlin
override fun onLoaderReset(loader: Loader<Cursor>) { // Delete the reference to the existing Cursor cursorAdapter?.swapCursor(null) }
Java
@Override public void onLoaderReset(Loader<Cursor> loader) { // Delete the reference to the existing Cursor cursorAdapter.swapCursor(null); }
Ora hai gli elementi chiave di un'app che associa una stringa di ricerca ai nomi dei contatti e restituisce il risultato in un ListView
. L'utente può fare clic sul nome di un contatto per selezionarlo.
Questo attiva un listener, in cui puoi lavorare ulteriormente con i dati del contatto. Ad esempio,
puoi recuperare i dettagli del contatto. Per informazioni su come eseguire questa operazione, continua con la lezione successiva, Recuperare i dettagli di un contatto.
Per scoprire di più sulle interfacce utente di ricerca, consulta la guida all'API Creare un'interfaccia di ricerca.
Le restanti sezioni di questa lezione mostrano altri modi per trovare i contatti nel provider di contatti.
Associare un contatto in base a un tipo specifico di dati
Questa tecnica ti consente di specificare il tipo di dati che vuoi associare. Il recupero per nome è un esempio specifico di questo tipo di query, ma puoi farlo anche per qualsiasi tipo di dati dettagliati associati a un contatto. Ad esempio, puoi recuperare i contatti con un determinato codice postale. In questo caso, la stringa di ricerca deve corrispondere ai dati memorizzati in una riga del codice postale.
Per implementare questo tipo di recupero, implementa prima il seguente codice, come indicato nelle sezioni precedenti:
- Richiedi l'autorizzazione a leggere il fornitore.
- Definire ListView e i layout degli elementi.
- Definisci un frammento che visualizza l'elenco dei contatti.
- Definisci le variabili globali.
- Inizializza il frammento.
- Configura CursorAdapter per ListView.
- Imposta il listener di contatti selezionato.
-
Definisci le costanti per gli indici di colonna del cursore.
Anche se stai recuperando i dati da una tabella diversa, l'ordine delle colonne nella proiezione è lo stesso, quindi puoi utilizzare gli stessi indici per il cursore.
- Definisci il metodo onItemClick().
- Inizializza il caricatore.
- Implementare onLoadFinished() e onLoaderReset().
I passaggi seguenti mostrano il codice aggiuntivo necessario per abbinare una stringa di ricerca a un determinato tipo di dati dettagliati e visualizzare i risultati.
Scegli il tipo di dati e la tabella
Per cercare un determinato tipo di dati di dettaglio, devi conoscere il valore del tipo MIME personalizzato
per il tipo di dati. Ogni tipo di dati ha un valore di tipo MIME unico definito da una costante CONTENT_ITEM_TYPE
nel sottotipo di ContactsContract.CommonDataKinds
associato al tipo di dati.
I sottoclassi hanno nomi che indicano il tipo di dati. Ad esempio, il sottoclasse per i dati email è ContactsContract.CommonDataKinds.Email
e il tipo MIME personalizzato per i dati email è definito dalla costante Email.CONTENT_ITEM_TYPE
.
Utilizza la tabella ContactsContract.Data
per la ricerca. Tutte le
costanti necessarie per la proiezione, la clausola di selezione e l'ordinamento sono definite o
ereditate da questa tabella.
Definisci una proiezione
Per definire una proiezione, scegli una o più colonne definite in
ContactsContract.Data
o le classi da cui eredita. Il provider di contatti esegue un join implicito tra ContactsContract.Data
e altre tabelle prima di restituire righe. Ad esempio:
Kotlin
@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 )
Java
@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 };
Definire i criteri di ricerca
Per cercare una stringa all'interno di un determinato tipo di dati, crea una clausola di selezione come segue:
-
Il nome della colonna contenente la stringa di ricerca. Questo nome varia in base al tipo di dati,
quindi devi trovare la sottoclasse di
ContactsContract.CommonDataKinds
che corrisponde al tipo di dati e poi scegliere il nome della colonna da quella sottoclasse. Ad esempio, per cercare indirizzi email, utilizza la colonnaEmail.ADDRESS
. - La stringa di ricerca stessa, rappresentata dal carattere "?" nella clausola di selezione.
-
Il nome della colonna che contiene il valore di tipo MIME personalizzato. Questo nome è sempre
Data.MIMETYPE
. -
Il valore del tipo MIME personalizzato per il tipo di dati. Come descritto in precedenza, si tratta della costante
CONTENT_ITEM_TYPE
nella classe secondariaContactsContract.CommonDataKinds
. Ad esempio, il valore del tipo MIME per i dati email èEmail.CONTENT_ITEM_TYPE
. Racchiudi il valore tra virgolette singole concatenando un carattere "'
" (virgolette singole) all'inizio e alla fine della costante. In caso contrario, il provider interpreta il valore 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
/* * 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}'"
Java
/* * 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 + "'";
Quindi, definisci le variabili che contengono l'argomento di selezione:
Kotlin
private var searchString: String? = null private val selectionArgs: Array<String> = arrayOf("")
Java
String searchString; String[] selectionArgs = { "" };
Implementa onCreateLoader()
Ora che hai specificato i dati desiderati e come trovarli, definisci una query nell'implementazione di onCreateLoader()
.
Restituisci un nuovo valore CursorLoader
da questo
metodo utilizzando la proiezione, l'espressione di testo selezione e l'array di selezione come
argomenti. Per un URI contenuto, utilizza
Data.CONTENT_URI
. Ad esempio:
Kotlin
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() }
Java
@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 ); }
Questi snippet di codice sono la base di una semplice ricerca inversa in base a un tipo specifico di dati dettagliati. Questa è la tecnica migliore da utilizzare se la tua app si concentra su un determinato tipo di dati, come le email, e vuoi consentire agli utenti di ottenere i nomi associati a un dato.
Associare un contatto in base a qualsiasi tipo di dati
Il recupero di un contatto in base a qualsiasi tipo di dati restituisce i contatti se uno dei relativi dati corrisponde alla stringa di ricerca, inclusi nome, indirizzo email, indirizzo postale, numero di telefono e così via. Ciò porta a un'ampia gamma di risultati di ricerca. Ad esempio, se la stringa di ricerca è "Rossi", la ricerca di qualsiasi tipo di dati restituisce il contatto "Giorgio Rossi" e anche i contatti che risiedono in "Via Rossi".
Per implementare questo tipo di recupero, implementa prima il seguente codice, come indicato nelle sezioni precedenti:
- Richiedi l'autorizzazione a leggere il fornitore.
- Definire ListView e i layout degli elementi.
- Definisci un frammento che visualizza l'elenco dei contatti.
- Definisci le variabili globali.
- Inizializza il frammento.
- Configura CursorAdapter per ListView.
- Imposta l'ascoltatore del contatto selezionato.
- Definisci una proiezione.
-
Definisci le costanti per gli indici delle colonne Cursore.
Per questo tipo di recupero, viene utilizzata la stessa tabella utilizzata nella sezione Associa un contatto per nome ed elenca i risultati. Utilizza anche gli stessi indici di colonna.
- Definisci il metodo onItemClick().
- Inizializza il caricatore.
- Implementa onLoadFinished() e onLoaderReset().
I passaggi che seguono mostrano il codice aggiuntivo necessario per associare una stringa di ricerca a qualsiasi tipo di dati e visualizzare i risultati.
Rimuovere i criteri di selezione
Non definire le costanti SELECTION
o la variabile mSelectionArgs
.
Questi parametri non vengono utilizzati in questo tipo di recupero.
Implementa onCreateLoader()
Implementa il metodo onCreateLoader()
restituendo un nuovo CursorLoader
.
Non è necessario convertire la stringa di ricerca in un pattern, perché il fornitore di contatti lo fa automaticamente. Utilizza
Contacts.CONTENT_FILTER_URI
come URI di base e aggiungi la stringa di ricerca chiamando
Uri.withAppendedPath()
. L'utilizzo di questo URI attiva automaticamente la ricerca di qualsiasi tipo di dati, come mostrato nell'esempio seguente:
Kotlin
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() }
Java
@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 ); }
Questi snippet di codice sono la base di un'app che esegue una ricerca ampia del provider di contatti. Questa tecnica è utile per le app che vogliono implementare funzionalità simili alla schermata dell'elenco contatti dell'app Contatti.