Recupero di un elenco di contatti

Questa lezione mostra come recuperare un elenco di contatti i cui dati corrispondono interamente o in parte a una stringa di ricerca, utilizzando le seguenti tecniche:

Fai corrispondere i nomi dei contatti
Recupera un elenco di contatti abbinando la stringa di ricerca a tutti o parte dei dati del nome del contatto. Il provider di contatti consente più istanze con lo stesso nome, pertanto questa tecnica può restituire un elenco di corrispondenze.
Associare un tipo specifico di dati, ad esempio un numero di telefono
Recupera un elenco di contatti abbinando la stringa di ricerca a un determinato tipo di dati dettagliati, ad esempio un indirizzo email. Ad esempio, questa tecnica consente di elencare tutti i contatti il cui indirizzo email corrisponde alla stringa di ricerca.
Associa qualsiasi tipo di dati
Recupera un elenco di contatti abbinando la stringa di ricerca a qualsiasi tipo di dati dettagliati, tra cui nome, numero di telefono, via, indirizzo email e così via. Ad esempio, questa tecnica consente di accettare qualsiasi tipo di dati per una stringa di ricerca e di 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 oggetto CursorLoader esegue la query su un thread separato dal thread dell'interfaccia utente. Ciò garantisce che la query non rallenti i tempi di risposta della UI e causi un'esperienza utente scadente. Per ulteriori informazioni, consulta il corso di formazione su Android Caricamento di dati in background.

Richiedi l'autorizzazione per leggere il provider

Per eseguire qualsiasi tipo di ricerca nel fornitore di contatti, 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.

Definizione di ListView e layout degli elementi

Per visualizzare i risultati di ricerca in un elemento ListView, sono necessari un file di layout principale che definisca l'intera UI, incluso ListView, e un file di layout degli elementi che definisca una riga di ListView. Ad esempio, potresti 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'elemento 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 ricevere la stringa indirettamente. Ad esempio, puoi offrire all'utente un'opzione per cercare i 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 prevede la scrittura del codice che utilizzi questa UI per visualizzare un elenco di contatti.

Definisci un frammento che mostri l'elenco dei contatti

Per visualizzare l'elenco dei contatti, inizia a definire un Fragment che viene caricato da un Activity. L'utilizzo di Fragment è una tecnica più flessibile, perché puoi utilizzare un solo 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 la lezione di addestramento Creare un'interfaccia utente dinamica con frammenti.

Per aiutarti a scrivere query sul provider di contatti, il framework Android fornisce una classe di 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 questo corso, includi 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 implementi l'interfaccia del caricatore LoaderManager.LoaderCallbacks. Inoltre, per contribuire a rilevare il contatto selezionato dall'utente dall'elenco dei risultati di ricerca, implementa l'interfaccia dell'adattatore AdapterView.OnItemClickListener. Ecco alcuni esempi:

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 {

Definire le variabili globali

Definisci le variabili globali che vengono 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, l'impostazione di minSdkVersion dell'app su 10 o versioni precedenti genera 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 aumenta l'interfaccia utente dell'oggetto Fragment nel metodo di callback onCreateView(). Ecco alcuni esempi:

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);
    }

Configurazione del CursorAdapter per ListView

Configura il SimpleCursorAdapter che associa i risultati della ricerca a ListView. Per ottenere l'oggetto ListView che mostra i contatti, devi chiamare Activity.findViewById() utilizzando l'attività principale di Fragment. Utilizza Context dell'attività genitore quando chiami setAdapter(). Ecco alcuni esempi:

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 visualizzi i risultati di una ricerca, in genere vuoi consentire all'utente di selezionare un singolo contatto per un'ulteriore elaborazione. Ad esempio, quando l'utente fa clic su un contatto, puoi visualizzare l'indirizzo del contatto su una mappa. Per fornire questa funzionalità, devi prima definire l'oggetto Fragment corrente come listener 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(). Ecco alcuni esempi:

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'attuale Fragment è OnItemClickListener per ListView, ora devi implementare il metodo richiesto onItemClick(), che gestisce l'evento di clic. Questo aspetto viene descritto in una delle sezioni successive.

Definisci una proiezione

Definisci una costante contenente le colonne che vuoi restituire dalla query. Ogni elemento in ListView mostra il nome visualizzato del contatto, che contiene il formato principale del nome del contatto. In Android 3.0 (versione 11 dell'API) e versioni successive, il nome di questa colonna è Contacts.DISPLAY_NAME_PRIMARY; nelle versioni precedenti, il nome è Contacts.DISPLAY_NAME.

La colonna Contacts._ID è utilizzata dal processo di associazione di SimpleCursorAdapter. Contacts._ID e LOOKUP_KEY vengono utilizzati insieme per costruire un URI di 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 delle colonne dei cursori

Per ottenere i dati da una singola colonna in un elemento Cursor, devi avere l'indice della colonna all'interno di Cursor. Puoi definire costanti per gli indici delle colonne Cursor, perché gli indici sono lo stesso dell'ordine dei nomi delle colonne nella proiezione. Ecco alcuni esempi:

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;

Specificare i criteri di selezione

Per specificare i dati che vuoi, crea una combinazione di espressioni di testo e variabili che indichino al fornitore 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 espressione possa contenere anche valori, la prassi migliore è rappresentare i valori con un segnaposto "?". Durante il recupero, il segnaposto viene sostituito con i valori di un array. L'uso di "?" come segnaposto garantisce che la specifica di ricerca sia generata tramite associazione anziché dalla compilazione SQL. Questa prassi elimina la possibilità di operazioni SQL injection dannose. Ecco alcuni esempi:

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 };

Definire il metodo onItemClick()

In una sezione precedente, hai impostato il listener dei clic sull'elemento per ListView. Ora implementa l'azione per il listener definendo il metodo AdapterView.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é stai utilizzando 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 mostrato 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 subito dopo la chiamata a initLoader().

In onCreateLoader(), configura il pattern di stringa di ricerca. Per trasformare una stringa in un pattern, inserisci i caratteri "%" (percent) per rappresentare una sequenza di zero o più caratteri o i caratteri "_" (trattino basso) per rappresentare un singolo carattere o entrambi. Ad esempio, il pattern "%Jefferson%" può trovare sia "Thomas Jefferson" sia "Jefferson Davis".

Restituisci un nuovo CursorLoader dal metodo. Per l'URI dei contenuti, 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
        );
    }

Implementare 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. Questa operazione aggiorna automaticamente ListView 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 del caricatore rileva che il risultato Cursor contiene dati inattivi. Elimina il riferimento SimpleCursorAdapter all'elemento Cursor esistente. In caso contrario, il framework del caricatore non ricicla Cursor, il che comporta una perdita di memoria. Ecco alcuni esempi:

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 corrispondono a una stringa di ricerca ai nomi dei contatti e restituiscono il risultato in un ListView. L'utente può fare clic sul nome di un contatto per selezionarlo. Viene attivato un listener, in cui puoi lavorare ulteriormente con i dati del contatto. Ad esempio, puoi recuperare i dettagli del contatto. Per scoprire come fare, continua con la lezione successiva, Recuperare i dettagli di un contatto.

Per scoprire di più sulle interfacce utente per la ricerca, consulta la guida API Creare un'interfaccia di ricerca.

Le altre sezioni di questa lezione mostrano altri modi per trovare contatti nel provider di contatti.

Associare un contatto in base a un tipo specifico di dati

Questa tecnica consente di specificare il tipo di dati da abbinare. Il recupero per nome è un esempio specifico di questo tipo di query, ma è possibile eseguirlo anche per qualsiasi tipo di dati dettagliato associato a un contatto. Ad esempio, puoi recuperare i contatti che hanno un codice postale specifico; in questo caso, la stringa di ricerca deve corrispondere ai dati archiviati in una riga del codice postale.

Per implementare questo tipo di recupero, implementa prima il codice seguente, come elencato nelle sezioni precedenti:

  • Richiedi l'autorizzazione per leggere il fornitore.
  • Definisci ListView e layout degli elementi.
  • Definisci un frammento che mostra l'elenco dei contatti.
  • Definire le variabili globali.
  • Inizializza il frammento.
  • Impostare il cursoreAdapter per ListView.
  • Imposta il listener di contatti selezionato.
  • Definisci le costanti per gli indici della colonna 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.
  • Implementa onLoadFinished() e onLoaderReset().

I passaggi seguenti mostrano il codice aggiuntivo necessario per associare 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 dettagliati, devi conoscere il valore del tipo MIME personalizzato per il tipo di dati. Ogni tipo di dati ha un valore di tipo MIME univoco definito da una costante CONTENT_ITEM_TYPE nella sottoclasse di ContactsContract.CommonDataKinds associata al tipo di dati. Le sottoclassi hanno nomi che ne indicano il tipo di dati; ad esempio, la sottoclasse per i dati email è ContactsContract.CommonDataKinds.Email, mentre il tipo MIME personalizzato per i dati delle 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 una join implicita tra ContactsContract.Data e altre tabelle prima di restituire le righe. Ecco alcuni esempi:

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
        };

Definisci i criteri di ricerca

Per cercare una stringa all'interno di un determinato tipo di dati, crea una clausola di selezione da:

  • Il nome della colonna contenente la stringa di ricerca. Questo nome varia a seconda del tipo di dati, perciò devi trovare la sottoclasse di ContactsContract.CommonDataKinds corrispondente al tipo di dati e scegliere il nome della colonna di quella sottoclasse. Ad esempio, per cercare indirizzi email, utilizza la colonna Email.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 di tipo MIME personalizzato per il tipo di dati. Come descritto in precedenza, questa è la costante CONTENT_ITEM_TYPE nella sottoclasse ContactsContract.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 variabile anziché come valore stringa. Non è necessario utilizzare un segnaposto per questo valore, perché stai utilizzando una costante anziché un valore fornito dall'utente.

Ecco alcuni esempi:

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 = { "" };

Implementare onCreateLoader()

Ora che hai specificato i dati che ti interessano e come trovarli, definisci una query nell'implementazione di onCreateLoader(). Restituisci un nuovo CursorLoader da questo metodo, utilizzando la proiezione, l'espressione di testo di selezione e l'array di selezione come argomenti. Per un URI di contenuti, utilizza Data.CONTENT_URI. Ecco alcuni esempi:

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 basata su un tipo specifico di dati dettagliati. Questa è la tecnica migliore da utilizzare se la tua app è incentrata su un determinato tipo di dati, ad esempio le email, e vuoi consentire agli utenti di ottenere i nomi associati a un dato.

Associa un contatto in base a qualsiasi tipo di dati

Il recupero di un contatto basato su qualsiasi tipo di dati restituisce i contatti se uno dei loro dati corrisponde a una stringa di ricerca, tra cui nome, indirizzo email, indirizzo postale, numero di telefono e così via. Questo porta a una vasta gamma di risultati di ricerca. Ad esempio, se la stringa di ricerca è "Rossi", la ricerca di qualsiasi tipo di dati restituisce il contatto "Mario Rossi", ma restituisce anche i contatti che vivono in "Roma".

Per implementare questo tipo di recupero, implementa prima il codice seguente, come elencato nelle sezioni precedenti:

  • Richiedi l'autorizzazione per leggere il fornitore.
  • Definisci ListView e layout degli elementi.
  • Definisci un frammento che mostra l'elenco dei contatti.
  • Definire le variabili globali.
  • Inizializza il frammento.
  • Impostare il cursoreAdapter per ListView.
  • Imposta il listener di contatti selezionato.
  • Definisci una proiezione.
  • Definisci le costanti per gli indici della colonna Cursore.

    Per questo tipo di recupero, utilizzi la stessa tabella che hai utilizzato nella sezione Associare un contatto per nome ed elencare i risultati. Utilizza anche gli stessi indici di colonna.

  • Definisci il metodo onItemClick().
  • Inizializza il caricatore.
  • Implementa onLoadFinished() e onLoaderReset().

I passaggi seguenti mostrano il codice aggiuntivo necessario per associare una stringa di ricerca a qualsiasi tipo di dati e visualizzare i risultati.

Rimuovi criteri di selezione

Non definire le costanti SELECTION o la variabile mSelectionArgs. Non vengono utilizzati in questo tipo di recupero.

Implementare onCreateLoader()

Implementa il metodo onCreateLoader(), restituendo un nuovo CursorLoader. Non è necessario convertire la stringa di ricerca in un pattern, poiché il provider di contatti lo fa automaticamente. Usa 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 un'ampia ricerca del provider di contatti. La tecnica è utile per le app che vogliono implementare funzionalità simili alla schermata dell'elenco contatti dell'app Persone.