Récupérer une liste de contacts

Cette leçon vous explique comment récupérer une liste de contacts dont les données correspondent à tout ou partie d'une chaîne de recherche à l'aide des techniques suivantes:

Faire correspondre les noms des contacts
Récupérez une liste de contacts en faisant correspondre la chaîne de recherche à tout ou partie des données des noms de contact. Le fournisseur de contacts autorise plusieurs instances du même nom. Cette technique peut donc renvoyer une liste de correspondances.
Faire correspondre un type de données spécifique, comme un numéro de téléphone
Récupérez une liste de contacts en faisant correspondre la chaîne de recherche à un type particulier de données détaillées, tel qu'une adresse e-mail. Par exemple, cette technique vous permet de répertorier tous les contacts dont l'adresse e-mail correspond à la chaîne de recherche.
Correspondance avec n'importe quel type de données
Récupérez une liste de contacts en faisant correspondre la chaîne de recherche à n'importe quel type de données détaillées, y compris le nom, le numéro de téléphone, l'adresse postale, l'adresse e-mail, etc. Par exemple, cette technique vous permet d'accepter n'importe quel type de données pour une chaîne de recherche, puis de répertorier les contacts pour lesquels les données correspondent à la chaîne.

Remarque:Tous les exemples de cette leçon utilisent un CursorLoader pour récupérer les données à partir du fournisseur de contacts. Un CursorLoader exécute sa requête sur un thread distinct du thread UI. Cela garantit que la requête ne ralentit pas les temps de réponse de l'interface utilisateur et ne nuit pas à l'expérience utilisateur. Pour en savoir plus, consultez la formation Android Charger des données en arrière-plan.

Demander l'autorisation de lire le fournisseur

Pour effectuer n'importe quel type de recherche dans le fournisseur de contacts, votre application doit disposer de l'autorisation READ_CONTACTS. Pour le demander, ajoutez cet élément <uses-permission> à votre fichier manifeste en tant qu'élément enfant de <manifest>:

    <uses-permission android:name="android.permission.READ_CONTACTS" />

Rechercher un contact par nom et lister les résultats

Cette technique tente d'établir une correspondance entre une chaîne de recherche et le nom d'un ou de plusieurs contacts du tableau ContactsContract.Contacts du fournisseur de contacts. Vous souhaitez généralement afficher les résultats dans un ListView pour permettre à l'utilisateur de faire son choix parmi les contacts correspondants.

Définir une ListView et des mises en page d'éléments

Pour afficher les résultats de recherche dans un ListView, vous avez besoin d'un fichier de mise en page principal qui définit l'ensemble de l'interface utilisateur, y compris le ListView, et d'un fichier de mise en page d'élément qui définit une ligne de ListView. Par exemple, vous pouvez créer le fichier de mise en page principal res/layout/contacts_list_view.xml avec le code XML suivant:

<?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"/>

Ce fichier XML utilise le widget Android ListView intégré android:id/list.

Définissez le fichier de mise en page de l'élément contacts_list_item.xml avec le code XML suivant:

<?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"/>

Ce fichier XML utilise le widget Android TextView intégré android:text1.

Remarque:Cette leçon ne décrit pas l'interface utilisateur permettant d'obtenir une chaîne de recherche de l'utilisateur, car vous pourriez vouloir obtenir la chaîne indirectement. Par exemple, vous pouvez donner à l'utilisateur la possibilité de rechercher des contacts dont le nom correspond à une chaîne dans un message texte entrant.

Les deux fichiers de mise en page que vous avez écrits définissent une interface utilisateur qui affiche un ListView. L'étape suivante consiste à écrire du code qui utilisera cette interface utilisateur pour afficher une liste de contacts.

Définir un fragment qui affiche la liste des contacts

Pour afficher la liste des contacts, commencez par définir un Fragment chargé par un Activity. L'utilisation d'un Fragment est une technique plus flexible, car vous pouvez utiliser un Fragment pour afficher la liste et un second Fragment pour afficher les détails d'un contact que l'utilisateur choisit dans la liste. Cette approche vous permet de combiner l'une des techniques présentées dans cette leçon avec celle de la leçon Récupérer les coordonnées d'un contact.

Pour apprendre à utiliser un ou plusieurs objets Fragment à partir d'un Activity, consultez la classe d'entraînement Créer une UI dynamique avec des fragments.

Pour vous aider à écrire des requêtes sur le fournisseur de contacts, le framework Android fournit une classe de contrats appelée ContactsContract, qui définit des constantes et des méthodes utiles pour accéder au fournisseur. Lorsque vous utilisez cette classe, vous n'avez pas besoin de définir vos propres constantes pour les URI de contenu, les noms de table ou les colonnes. Pour utiliser cette classe, incluez l'instruction suivante:

Kotlin

import android.provider.ContactsContract

Java

import android.provider.ContactsContract;

Étant donné que le code utilise un CursorLoader pour récupérer les données du fournisseur, vous devez spécifier qu'il met en œuvre l'interface de chargeur LoaderManager.LoaderCallbacks. En outre, pour vous aider à détecter le contact sélectionné par l'utilisateur dans la liste des résultats de recherche, implémentez l'interface d'adaptateur AdapterView.OnItemClickListener. Par exemple :

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 {

Définir des variables globales

Définissez les variables globales qui sont utilisées dans d'autres parties du code:

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

Remarque:Étant donné que Contacts.DISPLAY_NAME_PRIMARY nécessite Android 3.0 (API version 11) ou version ultérieure, définir la valeur minSdkVersion de votre application sur 10 ou une version antérieure génère un avertissement Android Lint dans Android Studio. Pour désactiver cet avertissement, ajoutez l'annotation @SuppressLint("InlinedApi") avant la définition de FROM_COLUMNS.

Initialiser le fragment

Initialisez Fragment. Ajoutez le constructeur public vide requis par le système Android et gonflez l'interface utilisateur de l'objet Fragment dans la méthode de rappel onCreateView(). Par exemple :

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

Configurer CursorAdapter pour la ListView

Configurez le SimpleCursorAdapter qui lie les résultats de la recherche à ListView. Pour obtenir l'objet ListView qui affiche les contacts, vous devez appeler Activity.findViewById() à l'aide de l'activité parente de Fragment. Utilisez le Context de l'activité parent lorsque vous appelez setAdapter(). Par exemple :

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

Définir l'écouteur du contact sélectionné

Lorsque vous affichez les résultats d'une recherche, vous souhaitez généralement permettre à l'utilisateur de sélectionner un seul contact pour un traitement plus poussé. Par exemple, lorsque l'utilisateur clique sur un contact, vous pouvez afficher son adresse sur une carte. Pour fournir cette fonctionnalité, vous avez d'abord défini le Fragment actuel en tant qu'écouteur de clics en spécifiant que la classe implémente AdapterView.OnItemClickListener, comme indiqué dans la section Définir un fragment qui affiche la liste des contacts.

Pour continuer à configurer l'écouteur, associez-le à ListView en appelant la méthode setOnItemClickListener() dans onActivityCreated(). Par exemple :

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

Comme vous avez spécifié que Fragment actuel est le OnItemClickListener pour ListView, vous devez maintenant implémenter la méthode requise onItemClick(), qui gère l'événement de clic. Ce processus est décrit dans une section suivante.

Définir une projection

Définissez une constante contenant les colonnes que vous souhaitez renvoyer à partir de votre requête. Chaque élément du ListView affiche le nom à afficher du contact, qui contient la forme principale du nom du contact. Dans Android 3.0 (version 11 d'API) et versions ultérieures, cette colonne s'appelle Contacts.DISPLAY_NAME_PRIMARY. Dans les versions antérieures, elle s'appelle Contacts.DISPLAY_NAME.

La colonne Contacts._ID est utilisée par le processus de liaison SimpleCursorAdapter. Contacts._ID et LOOKUP_KEY sont utilisés conjointement pour créer un URI de contenu pour le contact sélectionné par l'utilisateur.

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

        };

Définir des constantes pour les index de la colonne de curseur

Pour obtenir les données d'une colonne individuelle dans une propriété Cursor, vous avez besoin de l'index de la colonne dans Cursor. Vous pouvez définir des constantes pour les index des colonnes Cursor, car les index sont le même que l'ordre des noms de colonne dans votre projection. Par exemple :

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;

Spécifiez les critères de sélection

Pour spécifier les données souhaitées, créez une combinaison d'expressions de texte et de variables indiquant au fournisseur les colonnes de données à rechercher et les valeurs à rechercher.

Pour l'expression textuelle, définissez une constante qui liste les colonnes de recherche. Bien que cette expression puisse également contenir des valeurs, il est recommandé de les représenter à l'aide d'un espace réservé "?". Lors de la récupération, l'espace réservé est remplacé par les valeurs d'un tableau. L'utilisation de "?" comme espace réservé garantit que la spécification de recherche est générée par liaison plutôt que par compilation SQL. Cette pratique élimine le risque d'injection SQL malveillante. Par exemple :

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

Définir la méthode onItemClick()

Dans une section précédente, vous avez défini l'écouteur de clics de l'élément pour ListView. Implémentez maintenant l'action pour l'écouteur en définissant la méthode 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.
         */
    }

Initialiser le chargeur

Étant donné que vous utilisez un CursorLoader pour récupérer des données, vous devez initialiser le thread d'arrière-plan et les autres variables qui contrôlent la récupération asynchrone. Effectuez l'initialisation dans onCreate(), comme indiqué dans l'exemple suivant:

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

Implémenter onCreateLoader()

Implémentez la méthode onCreateLoader(), qui est appelée par le framework du chargeur immédiatement après l'appel de initLoader().

Dans onCreateLoader(), configurez le modèle de chaîne de recherche. Pour transformer une chaîne en modèle, insérez des caractères "%" (pourcentage) pour représenter une séquence de zéro ou plusieurs caractères, ou des traits de soulignement "_" (trait de soulignement) pour représenter un seul caractère, ou les deux. Par exemple, le modèle "%Jefferson%" correspond à la fois à "Thomas Jefferson" et à "Jefferson Davis".

Renvoyez un nouveau CursorLoader à partir de la méthode. Pour l'URI de contenu, utilisez Contacts.CONTENT_URI. Cet URI fait référence à l'ensemble de la table, comme illustré dans l'exemple suivant:

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

Implémenter onLoadFinished() et onLoaderReset()

Implémentez la méthode onLoadFinished(). Le framework du chargeur appelle onLoadFinished() lorsque le fournisseur de contacts renvoie les résultats de la requête. Dans cette méthode, placez le résultat Cursor dans SimpleCursorAdapter. Cette opération met automatiquement à jour ListView avec les résultats de recherche:

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

La méthode onLoaderReset() est appelée lorsque le framework du chargeur détecte que le résultat Cursor contient des données obsolètes. Supprimez la référence SimpleCursorAdapter au Cursor existant. Si vous ne le faites pas, le framework du chargeur ne recyclera pas le Cursor, ce qui entraînera une fuite de mémoire. Par exemple :

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

    }

Vous disposez maintenant des éléments clés d'une application qui met en correspondance une chaîne de recherche pour contacter les noms et renvoie le résultat dans un ListView. L'utilisateur peut cliquer sur le nom d'un contact pour le sélectionner. Cela déclenche un écouteur dans lequel vous pouvez travailler davantage sur les données du contact. Vous pouvez par exemple récupérer les coordonnées du contact. Pour savoir comment procéder, passez à la leçon suivante, Récupérer les coordonnées d'un contact.

Pour en savoir plus sur les interfaces utilisateur de recherche, consultez le guide de l'API Créer une interface de recherche.

Les autres sections de cette leçon présentent d'autres méthodes pour rechercher des contacts dans Contacts Provider.

Faire correspondre un contact en fonction d'un type de données spécifique

Cette technique vous permet de spécifier le type de données que vous souhaitez mettre en correspondance. La récupération par nom est un exemple spécifique de ce type de requête, mais vous pouvez également l'effectuer pour n'importe quel type de données détaillées associés à un contact. Par exemple, vous pouvez récupérer des contacts associés à un code postal spécifique. Dans ce cas, la chaîne de recherche doit correspondre aux données stockées dans une ligne de code postal.

Pour implémenter ce type de récupération, commencez par implémenter le code suivant, comme indiqué dans les sections précédentes:

  • Demandez l'autorisation de lire le fournisseur.
  • Définir des mises en page de type ListView et d'élément
  • Définissez un fragment qui affiche la liste des contacts.
  • Définissez des variables globales.
  • Initialisez le fragment.
  • Configurez CursorAdapter pour la ListView.
  • Définir l'écouteur du contact sélectionné.
  • Définit des constantes pour les index de la colonne de curseur.

    Bien que vous récupériez les données d'une autre table, l'ordre des colonnes dans la projection est le même. Vous pouvez donc utiliser les mêmes index pour le curseur.

  • Définissez la méthode onItemClick().
  • Initialisez le chargeur.
  • Implémentez onLoadFinished() et onLoaderReset().

Les étapes suivantes vous montrent le code supplémentaire dont vous avez besoin pour faire correspondre une chaîne de recherche à un type particulier de données détaillées et afficher les résultats.

Choisir le type de données et le tableau

Pour rechercher un type particulier de données détaillées, vous devez connaître la valeur du type MIME personnalisé pour le type de données. Chaque type de données possède une valeur de type MIME unique définie par une constante CONTENT_ITEM_TYPE dans la sous-classe de ContactsContract.CommonDataKinds associée au type de données. Les noms des sous-classes indiquent leur type de données. Par exemple, la sous-classe pour les données de messagerie est ContactsContract.CommonDataKinds.Email, et le type MIME personnalisé pour les données de messagerie est défini par la constante Email.CONTENT_ITEM_TYPE.

Utilisez la table ContactsContract.Data pour votre recherche. Toutes les constantes dont vous avez besoin pour la projection, la clause de sélection et l'ordre de tri sont définies dans cette table ou en héritent.

Définir une projection

Pour définir une projection, choisissez une ou plusieurs des colonnes définies dans ContactsContract.Data ou les classes dont elle hérite. Le fournisseur de contacts effectue une jointure implicite entre ContactsContract.Data et d'autres tables avant de renvoyer des lignes. Par exemple :

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

Définir des critères de recherche

Pour rechercher une chaîne dans un type de données particulier, créez une clause de sélection à l'aide des éléments suivants:

  • Nom de la colonne contenant la chaîne de recherche. Ce nom varie selon le type de données. Vous devez donc rechercher la sous-classe de ContactsContract.CommonDataKinds correspondant au type de données, puis choisir le nom de colonne de cette sous-classe. Par exemple, pour rechercher des adresses e-mail, utilisez la colonne Email.ADDRESS.
  • Chaîne de recherche proprement dite, représentée par le caractère "?" dans la clause de sélection.
  • Nom de la colonne contenant la valeur du type MIME personnalisé. Ce nom est toujours Data.MIMETYPE.
  • Valeur de type MIME personnalisé pour le type de données. Comme décrit précédemment, il s'agit de la constante CONTENT_ITEM_TYPE dans la sous-classe ContactsContract.CommonDataKinds. Par exemple, la valeur du type MIME pour les données de messagerie est Email.CONTENT_ITEM_TYPE. Placez la valeur entre guillemets simples en concaténant un caractère "'" (guillemet simple) au début et à la fin de la constante. Sinon, le fournisseur interprète la valeur comme un nom de variable plutôt que comme une valeur de chaîne. Vous n'avez pas besoin d'utiliser d'espace réservé pour cette valeur, car vous utilisez une constante plutôt qu'une valeur fournie par l'utilisateur.

Par exemple :

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 + "'";

Ensuite, définissez les variables qui contiennent l'argument de sélection:

Kotlin

    private var searchString: String? = null
    private val selectionArgs: Array<String> = arrayOf("")

Java

    String searchString;
    String[] selectionArgs = { "" };

Implémenter onCreateLoader()

Maintenant que vous avez spécifié les données dont vous avez besoin et comment les trouver, définissez une requête dans votre implémentation de onCreateLoader(). Renvoyez un nouveau CursorLoader à partir de cette méthode en utilisant votre projection, votre expression de texte de sélection et votre tableau de sélection comme arguments. Pour un URI de contenu, utilisez Data.CONTENT_URI. Par exemple :

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

Ces extraits de code constituent la base d'une recherche inversée simple basée sur un type spécifique de données détaillées. Il s'agit de la meilleure technique à utiliser si votre application se concentre sur un type de données particulier, comme les e-mails, et que vous souhaitez permettre aux utilisateurs d'obtenir les noms associés à une donnée.

Faire correspondre un contact selon n'importe quel type de données

La récupération d'un contact basée sur n'importe quel type de données renvoie des contacts si l'une de leurs données correspond à la chaîne de recherche, y compris le nom, l'adresse e-mail, l'adresse postale, le numéro de téléphone, etc. Cela se traduit par un large éventail de résultats de recherche. Par exemple, si la chaîne de recherche est "Dupont", la recherche d'un type de données renvoie le contact "Jean Dupont", ainsi que les contacts qui vivent dans la rue "Ducourneau".

Pour implémenter ce type de récupération, commencez par implémenter le code suivant, comme indiqué dans les sections précédentes:

  • Demandez l'autorisation de lire le fournisseur.
  • Définir des mises en page de type ListView et d'élément
  • Définissez un fragment qui affiche la liste des contacts.
  • Définissez des variables globales.
  • Initialisez le fragment.
  • Configurez CursorAdapter pour la ListView.
  • Définir l'écouteur du contact sélectionné.
  • Définissez une projection.
  • Définit des constantes pour les index de la colonne de curseur.

    Pour ce type de récupération, vous utilisez la même table que celle que vous avez utilisée dans la section Faire correspondre un contact par nom et répertorier les résultats. Utilisez également les mêmes index de colonne.

  • Définissez la méthode onItemClick().
  • Initialisez le chargeur.
  • Implémentez onLoadFinished() et onLoaderReset().

Les étapes suivantes vous montrent le code supplémentaire dont vous avez besoin pour faire correspondre une chaîne de recherche à n'importe quel type de données et afficher les résultats.

Supprimer les critères de sélection

Ne définissez pas les constantes SELECTION ni la variable mSelectionArgs. Ces éléments ne sont pas utilisés dans ce type de récupération.

Implémenter onCreateLoader()

Implémentez la méthode onCreateLoader() en renvoyant un nouvel objet CursorLoader. Vous n'avez pas besoin de convertir la chaîne de recherche en format, car le fournisseur de contacts le fait automatiquement. Utilisez Contacts.CONTENT_FILTER_URI comme URI de base et ajoutez-y votre chaîne de recherche en appelant Uri.withAppendedPath(). L'utilisation de cet URI déclenche automatiquement la recherche de tout type de données, comme illustré dans l'exemple suivant:

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

Ces extraits de code constituent la base d'une application qui effectue une recherche large sur Contacts Provider. Cette technique est utile pour les applications qui souhaitent implémenter une fonctionnalité semblable à l'écran de la liste de contacts de l'application Contacts.