Ludovic ROLAND - Le blog

Blog technique sur mes expériences de développeur

Android : accéder aux contacts

| Comments

Comme le disait Zidane il y a quelques années dans une pub pour Canal Sat’ “Eh oui, c’est la reprise !”. Pour cette reprise, l’objectif est de faire des articles plus régulièrement sur ce blog. Soyons fous, on va tenter de publier un nouvel article par semaine !

Dans ce premier article de la rentrée, nous allons voir comment accéder aux différentes informations des contacts enregistrés sur un téléphone Android.

Avant de commencer…

La petite application d’exemple que nous allons développer au cours de ce tutoriel s’appuie sur la classe ContentResolver. Cette classe permet, notamment, via des requêtes de demander des informations aux systèmes comment par exemple la liste des SMS ou la liste des contacts.

La requête

La méthode query

Afin de récupérer la liste des contacts, nous allons donc devoir utiliser la classe ContentResolver et plus précisément la méthode query. Je vous propose de faire un petit tour sur la documentation officielle pour voir les paramètres que prend cette méthode.

Comme vous pouvez le constater, il existe deux signatures pour cette méthode. Intéressons-nous à celle qui accepte le moins de paramètres :

Signature de la méthode query
1
public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

Cette méthode accepte cinq paramètres. Dans le cadre de ce tutoriel, nous allons nous attarder uniquement sur les deux premiers :

  • uri : il s’agit de l’URI à interoger pour récupérer les résultats que nous souhaitons. Via cette URI, nous allons pouvoir préciser la nature de ce que nous souhaitons récupérer comme par exemple des contacts, des SMS envoyés ou des SMS reçus.
  • projection : il s’agit d’un tableau permettant de filtrer les informations que l’on souhaite recevoir. Par filtre, je n’entends pas quelque chose du style les SMS envoyés par le Gérard, mais plutôt je ne veux que le nom et l’identifiant des contacts. Filtrer les informations à recevoir permet d’optimiser légèrement la requête qui est faite et gagner en performance.

Première version…

Je vous propose de débuter en douceur en créant une méthode privée dans un Fragment ou une Activity qui accepte pour paramètre un objet de type ContentResolver :

La méthode retrieveContacts
1
2
3
private void retrieveContacts(ContentResolver contentResolver)
{
}

Dans mon cas, cette méthode sera appelée depuis la méthode onCreateView d’un Fragment, aussi, elle sera appelée de la façon suivante : retrieveContacts(getActivity().getContentResolver());. Je vous laisse bien évidemment faire les vérifications d’usage quant à l’appel de la méthode getActivity() ;) .

Dans le cadre de cette première version, nous allons nous contenter d’appeler la méthode query avec le paramètre minimum, à savoir l’URI. L’URI permettant de récupérer les contacts est accessible via la constante ContactsContract.Contacts.CONTENT_URI.

Notre appel à la méthode query ressemble donc à ça :

1
contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

Complétons notre méthode retrieveContacts en exploitant le résultat de la méthode query qui est un cursor :

La méthode retrieveContacts
1
2
3
4
private void retrieveContacts(ContentResolver contentResolver)
{
  final Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
}

Avant d’exploiter le cursor, je vous propose de vérifier qu’il n’est pas null. Si c’est le cas, nous quittons la méthode :

1
2
3
4
5
if (cursor == null)
{
  Log.e("retrieveContacts", "Cannot retrieve the contacts");
  return;
}

Dans le cas contraire, nous allons vérifier qu’il contient au moins un résultat en le déplaçant sur son premier élément :

1
2
3
4
if (cursor.moveToFirst() == true)
{

}

S’il contient au moins un élément, nous allons parcourir tous les éléments à l’aide d’une boucle do...while :

1
2
3
4
5
6
7
8
if (cursor.moveToFirst() == true)
{
  do
  {

  }
  while (cursor.moveToNext() == true);
}

Finalement, nous allons, avant de quitter notre méthode retrieveContacts, fermer le cursor :

1
2
3
4
if (cursor.isClosed() == false)
{
  cursor.close();
}

Voici ce à quoi doit ressembler la méthode retrieveContacts pour le moment :

La méthode retrieveContacts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void retrieveContacts(ContentResolver contentResolver)
{
  final Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

  if (cursor == null)
  {
    Log.e("retrieveContacts", "Cannot retrieve the contacts");
    return;
  }

  if (cursor.moveToFirst() == true)
  {
    do
    {

    }
    while (cursor.moveToNext() == true);
  }

  if (cursor.isClosed() == false)
  {
    cursor.close();
  }
}

Il convient donc de compléter la boucle do...while afin d’exploiter réellement les éléments du cursor. Pour ça, nous allons devoir exploiter l’une des méthodes suivantes du cursor : getDouble(), getFloat(), getInt(), getLong(), getShort(), getString(). Chacune de ces méthodes prend un seul paramètre qui correspond à l’index de la colonne dont on souhaite récupérer l’information.

Par exemple, si je souhaite récupérer le nom du contact voici ce que l’on doit écrire :

1
cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));

Par exemple, si je souhaite récupérer l’identifiant du contact, voici ce que l’on doit écrire :

1
cursor.getLong(cursor.getColumnIndex(ContactsContract.Data._ID));

La liste complète des constantes est disponible dans la documentation officielle.

Je vous propose alors de compléter notre méthode retrieveContacts en affichant dans le logcat l’identifiant et le nom des contacts :

La méthode retrieveContacts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void retrieveContacts(ContentResolver contentResolver)
{
  final Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

  if (cursor == null)
  {
    Log.e("retrieveContacts", "Cannot retrieve the contacts");
    return;
  }

  if (cursor.moveToFirst() == true)
  {
    do
    {
      final long id = cursor.getLong(cursor.getColumnIndex(ContactsContract.Data._ID));
      final String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));

      Log.d("retrieveContacts", "The contact with id + '" + id + "' and name '" + name + "' has been retrieved");
    }
    while (cursor.moveToNext() == true);
  }

  if (cursor.isClosed() == false)
  {
    cursor.close();
  }
}

Si vous exécutez l’application Android, vous allez vous rendre compte que tout fonctionne, mais on peut déjà y apercevoir deux défauts :

  • c’est lent ;
  • on récupère bien plus de contacts que ceux réellement dans le téléphone.

Pour le premier point négatif, nous allons pouvoir procéder à une petite optimisation. Comme je vous le disais un peu plus haut, il est possible d’optimiser un peu l’appel à la méthode query du ContentResolver en précisant en filtrant les colonnes que nous souhaitons récupérer. Dans le cadre de notre première version de la méthode retrieveContacts, nous nous intéressons uniquement à deux colonnes : ContactsContract.Data._ID et ContactsContract.Data.DISPLAY_NAME.

C’est pourquoi, notre appel à la méthode query peut être légèrement modifié :

1
contentResolver.query(ContactsContract.Contacts.CONTENT_URI, new String[] { ContactsContract.Data.DISPLAY_NAME, ContactsContract.Data._ID}, null, null, null);

Le reste ne changeant pas, voici ce à quoi ressemble la première version de notre méthode retrieveContacts :

La méthode retrieveContacts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void retrieveContacts(ContentResolver contentResolver)
{
  final Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, new String[] { ContactsContract.Data.DISPLAY_NAME, ContactsContract.Data._ID}, null, null, null);

  if (cursor == null)
  {
    Log.e("retrieveContacts", "Cannot retrieve the contacts");
    return;
  }

  if (cursor.moveToFirst() == true)
  {
    do
    {
      final long id = cursor.getLong(cursor.getColumnIndex(ContactsContract.Data._ID));
      final String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));

      Log.d("retrieveContacts", "The contact with id + '" + id + "' and name '" + name + "' has been retrieved");
    }
    while (cursor.moveToNext() == true);
  }

  if (cursor.isClosed() == false)
  {
    cursor.close();
  }
}

Deuxième version…

Dans le cadre de la deuxième version de notre méthode retrieveContacts nous allons nous attaquer au problème lié au fait que l’on récupère bien plus de contacts que ceux réellement dans le téléphone. Malheureusement je n’ai pas solution miracle. C’est pourquoi, dans le cadre de cet article, nous allons récupérer uniquement le nom des contacts qui possèdent au moins un numéro de téléphone.

Pour savoir si un contact possède un numéro de téléphone, il convient d’accéder à la colonne du cursor dont l’index est donné par la constante ContactsContract.Data.HAS_PHONE_NUMBER. Nous allons donc devoir modifier notre méthode query pour ajouter cette information dans la liste des champs auxquels nous souhaitons accéder. Nous allons également devoir exploiter le résultat de ce nouveau champ. C’est assez simple, on récupère un entier. S’il est supérieur à zéro, le contact possède un numéro de téléphone.

Voici alors ce à quoi pourrait ressembler notre méthode retrieveContacts dans sa version 2 :

La méthode retrieveContacts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private void retrieveContacts(ContentResolver contentResolver)
{
  final Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, new String[] { ContactsContract.Data.DISPLAY_NAME, ContactsContract.Data._ID, ContactsContract.Contacts.HAS_PHONE_NUMBER }, null, null, null);

  if (cursor == null)
  {
    Log.e("retrieveContacts", "Cannot retrieve the contacts");
    return;
  }

  if (cursor.moveToFirst() == true)
  {
    do
    {
      final long id = cursor.getLong(cursor.getColumnIndex(ContactsContract.Data._ID));
      final String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
      final int hasPhoneNumber = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.HAS_PHONE_NUMBER));

      if (hasPhoneNumber > 0)
      {
        Log.d("retrieveContacts", "The contact with id + '" + id + "' and name '" + name + "' has been retrieved");
      }
    }
    while (cursor.moveToNext() == true);
  }

  if (cursor.isClosed() == false)
  {
    cursor.close();
  }
}

Si vous exécutez la méthode, vous allez alors vous rendre compte qu’il y a encore quelques doublons au niveau des ContactsContract.Data.DISPLAY_NAME. Ces doublons sont tout simplement dû au fait qu’un contact peut avoir plusieurs numéros de téléphone. Pour chacun de ces numéros, le même ContactsContract.Data.DISPLAY_NAME est utilisé. Nous allons donc modifier notre méthode et supprimer ces doublons à l’aide d’un HashSet. Pour rappel, un HashSetest une implémentantation de l’interface Set qui est une structure de données ne supportant pas les doublons.

Voici ce à quoi pourrait ressembler notre méthode :

La méthode retrieveContacts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private void retrieveContacts(ContentResolver contentResolver)
{
  final Set<String> contacts = new HashSet<String>();
  final Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, new String[] { ContactsContract.Data.DISPLAY_NAME, ContactsContract.Data._ID, ContactsContract.Contacts.HAS_PHONE_NUMBER }, null, null, null);

  if (cursor == null)
  {
    Log.e("retrieveContacts", "Cannot retrieve the contacts");
    return;
  }

  if (cursor.moveToFirst() == true)
  {
    do
    {
      final long id = Long.parseLong(cursor.getString(cursor.getColumnIndex(ContactsContract.Data._ID)));
      final String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
      final int hasPhoneNumber = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.HAS_PHONE_NUMBER));

      if (hasPhoneNumber > 0)
      {
        contacts.add(name);
      }
    }
    while (cursor.moveToNext() == true);
  }

  if (cursor.isClosed() == false)
  {
    cursor.close();
  }

  for (final String contact : contacts)
  {
    Log.d("retrieveContacts", "The contact '" + contact + "' has been retrieved");
  }
}

Un peu d’UI

Maintenant que nous sommes au point avec notre liste de contacts, je vous propose de les afficher dans une ListView. Pour celà, nous allons devoir modifier notre méthode pour qu’elle renvoie maintenant notre liste de contacts sous la forme d’un ArrayList.

La méthode retrieveContacts

La première étape consiste donc à modifier notre méthode retrieveContacts. Dorénavant, sa signature est la suivante :

La méthode retrieveContacts
1
2
3
private List<String> retrieveContacts(ContentResolver contentResolver)
{
}

Celà nous oblige à retourner un résultat à deux endroits de la fonction :

  • dans le if qui vérifie que le cursor n’est pas null ;
  • à la fin de la méthode.

Pour le if allons au plus simple et renvoyons null :

1
2
3
4
5
if (cursor == null)
{
  Log.e("retrieveContacts", "Cannot retrieve the contacts");
  return null;
}

Pour le renvoie en fin de méthode, la seule difficulté est de transformer notre HashSet en ArrayList. Mais le constructeur d’un ArrayList acceptant une collection, la difficulté est très rapidement contournée :

1
return new ArrayList<String>(contacts);

Finalement, après avoir ajouté une petite ligne permettant de trier les contacts, voici ce à quoi ressemble notre méthode retrieveContacts :

La méthode retrieveContacts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private List<String> retrieveContacts(ContentResolver contentResolver)
{
  final Set<String> contacts = new HashSet<String>();
  final Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, new String[] { ContactsContract.Data.DISPLAY_NAME, ContactsContract.Data._ID, ContactsContract.Contacts.HAS_PHONE_NUMBER }, null, null, null);

  if (cursor == null)
  {
    Log.e("retrieveContacts", "Cannot retrieve the contacts");
    return null;
  }

  if (cursor.moveToFirst() == true)
  {
    do
    {
      final long id = Long.parseLong(cursor.getString(cursor.getColumnIndex(ContactsContract.Data._ID)));
      final String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
      final int hasPhoneNumber = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.HAS_PHONE_NUMBER));

      if (hasPhoneNumber > 0)
      {
        contacts.add(name);
      }
    }
    while (cursor.moveToNext() == true);
  }

  if (cursor.isClosed() == false)
  {
    cursor.close();
  }

  final List<String> sortedContacts = new ArrayList<String>(contacts);
  Collections.sort(sortedContacts);

  return sortedContacts;
}

L’affichage des contacts

Pour afficher les contacts, nous allons utiliser une ListView avec un ArrayAdapter. Voici alors ce à quoi ressemble le layout du Fragment :

Le layout fragment_main.xml
1
2
3
4
5
6
<ListView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@android:id/list"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
/>

Finalement, voici ce à quoi ressemble la méthode onCreateView du Fragment qui affiche la liste des contacts :

La méthode onCreateView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
  View rootView = inflater.inflate(R.layout.fragment_main, container, false);

  final ListView list = (ListView) rootView.findViewById(android.R.id.list);
  final List<String> contacts = retrieveContacts(getActivity().getContentResolver());

  if (contacts != null)
  {
    list.setAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, contacts));
  }

  return rootView;
}

Après exécution de l’application, vous devriez avoir quelque chose comme ça :

Ajout de la photo du contact

Pour terminer cet article, je vous propose de modifier notre application pour qu’elle puisse également afficher la photo du contact. Nous allons donc devoir faire quelques modifications à notre petit programme !

La méthode getPhoto

Avant de modifier la méthode retrieveContacts, il convient d’écrire une petite méthode permettant de récupérer la photo d’un contact en fonction de son identifiant. Voici la signature de la méthode getPhoto :

La méthode getPhoto
1
2
3
private Bitmap getPhoto(ContentResolver contentResolver, long contactId)
{
}

Cette méthode prend donc un contentResolver ainsi que l’identifiant du contact dont on souhaite récupérer la photo.

Dans cette méthode, nous allons une nouvelle fois utiliser la méthode query du contentResolver, mais cette fois, la construction de l’URI est un peu plus compliquée car construite en deux temps. Dans un premier temps, nous allons devoir dire que nous souhaitons récupérer les informations relatives à un contact spécifique à l’aide de la méthode statique withAppendedId de la classe ContentUris :

1
final Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);

Nous allons ensuite spécifier que c’est aux photos que nous souhaitons accéder via la méthode statique withAppendedPath de la classe Uri :

1
final Uri photoUri = Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);

Finalement, nous allons limiter notre recherche au champ ContactsContract.Contacts.Photo.DATA15 qui correspond à la photo de profile :

1
final Cursor cursor = contentResolver.query(photoUri, new String[] { ContactsContract.Contacts.Photo.DATA15 }, null, null, null);

Nous allons donc pouvoir exploiter le résultat, mais avant ça, vérifions que le cursor n’est pas null :

1
2
3
4
5
if (cursor == null)
{
  Log.e("getPhoto", "Cannot retrieve the photo of the contact with id '" + contactId + "'");
  return null;
}

Nous allons maintenant vérifier que le cursor possède au moins un élément en le déplaçant dessus. S’il possède bien un élément, nous allons en conclure qu’il s’agit de la photo que nous voulons. Nous allons alors récupérer le tableau de byte qui compose la photo à l’aide de la méthode getBlob du cursor. Finalement, nous allons la transformer en Bitmap :

1
2
3
4
5
6
7
8
9
if (cursor.moveToFirst() == true)
{
  final byte[] data = cursor.getBlob(0);

  if (data != null)
  {
    final Bitmap photo = BitmapFactory.decodeStream(new ByteArrayInputStream(data));
  }
}

Finalement, avant de retourner notre photo, il convient de fermer le cursor :

1
2
3
4
if (cursor.isClosed() == false)
{
  cursor.close();
}

Finalement, voici la méthode complète :

La méthode getPhoto
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private Bitmap getPhoto(ContentResolver contentResolver, long contactId)
{
  Bitmap photo = null;
  final Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
  final Uri photoUri = Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
  final Cursor cursor = contentResolver.query(photoUri, new String[] { ContactsContract.Contacts.Photo.DATA15 }, null, null, null);

  if (cursor == null)
  {
    Log.e("getPhoto", "Cannot retrieve the photo of the contact with id '" + contactId + "'");
    return null;
  }

  if (cursor.moveToFirst() == true)
  {
    final byte[] data = cursor.getBlob(0);

    if (data != null)
    {
      photo = BitmapFactory.decodeStream(new ByteArrayInputStream(data));
    }
  }

  if (cursor.isClosed() == false)
  {
    cursor.close();
  }

  return photo;
}

La méthode retrieveContacts

Nous allons maintenant modifier notre méthode retrieveContacts pour qu’elle ne renvoie plus une Collection de String, mais une Collection de Map. En effet, pour afficher nos contacts, nous utiliserons un SimpleAdapter. Nous allons également lui faire consommer notre méthode getPhoto fraîchement écrite.

Avant toute chose, voici la nouvelle signature de la méthode retrieveContacts :

La méthode retrieveContacts
1
2
3
private List<Map<String, Object>> retrieveContacts(ContentResolver contentResolver)
{
}

Nous allons donc devoir alimenter la liste à retourner. Avant toute chose, il convient de modifier le type de notre liste de contacts :

1
final List<Map<String, Object>> contacts = new ArrayList<Map<String, Object>>();

Pour le reste tout se passe dans la boucle do...while. Pour chaque contact, on récupère le nom et la photo que l’on stocke dans une Map, puis on stocke la Map dans notre variable contacts :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
do
{
  final long id = Long.parseLong(cursor.getString(cursor.getColumnIndex(ContactsContract.Data._ID)));
  final String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
  final int hasPhoneNumber = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.HAS_PHONE_NUMBER));

  if (hasPhoneNumber > 0)
  {
    final Bitmap photo = getPhoto(contentResolver, id);

    final Map<String, Object> contact = new HashMap<String, Object>();
    contact.put("name", name);
    contact.put("photo", photo);

    contacts.add(contact);
  }
}
while (cursor.moveToNext() == true);

Voici alors ce à quoi ressemble la méthode complète :

La méthode retrieveContacts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private List<Map<String, Object>> retrieveContacts(ContentResolver contentResolver)
{
  final List<Map<String, Object>> contacts = new ArrayList<Map<String, Object>>();
  final Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, new String[] { ContactsContract.Data.DISPLAY_NAME,
      ContactsContract.Data._ID, ContactsContract.Contacts.HAS_PHONE_NUMBER }, null, null, null);

  if (cursor == null)
  {
    Log.e("retrieveContacts", "Cannot retrieve the contacts");
    return null;
  }

  if (cursor.moveToFirst() == true)
  {
    do
    {
      final long id = Long.parseLong(cursor.getString(cursor.getColumnIndex(ContactsContract.Data._ID)));
      final String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
      final int hasPhoneNumber = cursor.getInt(cursor.getColumnIndex(ContactsContract.Data.HAS_PHONE_NUMBER));

      if (hasPhoneNumber > 0)
      {
        final Bitmap photo = getPhoto(contentResolver, id);

        final Map<String, Object> contact = new HashMap<String, Object>();
        contact.put("name", name);
        contact.put("photo", photo);

        contacts.add(contact);
      }
    }
    while (cursor.moveToNext() == true);
  }

  if (cursor.isClosed() == false)
  {
    cursor.close();
  }

  return contacts;
}

Attention : il y a une régression (volontaire) ! Ici, il y a un retour des doublons et les contacts ne sont plus triés ! Je vous laisse prendre les mesures nécessaires à la correction de ce problème ;) !

L’affichage des contacts

Il est temps de conclure par l’affichage des contacts ! La première étape consiste à créer le layout d’un contact qui permettra d’afficher la photo et le nom du contact. Voici ce que vous pourriez faire :

Le layout contact.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
>
  <ImageView
    android:id="@+id/photo"
    android:layout_width="150dip"
    android:layout_height="150dip"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter"
  />

  <TextView
    android:id="@+id/name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:layout_marginLeft="10dip"
  />
</LinearLayout>

Nous allons donc modifier la méthode onCreateView du Fragment pour utiliser maintenant un SimpleAdapter :

final List<Map<String, Object>> contacts = retrieveContacts(getActivity().getContentResolver());

1
2
3
4
5
6
if (contacts != null)
{
  final SimpleAdapter adapter = new SimpleAdapter(getActivity(), contacts, R.layout.contact, new String[] { "name", "photo" }, new int[] { R.id.name,
      R.id.photo });
  list.setAdapter(adapter);
}

Le problème c’est que si on s’arrête là, ça ne fonctionnera pas ! En effet, nous allons devoir écrire un ViewBinder permettant à notre SimpleAdapter d’afficher correctement les photos. Son code étant relativement simple, je vous le fournis directement :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
adapter.setViewBinder(new ViewBinder()
{

  @Override
  public boolean setViewValue(View view, Object data, String textRepresentation)
  {
    if ((view instanceof ImageView) & (data instanceof Bitmap))
    {
      final ImageView image = (ImageView) view;
      final Bitmap photo = (Bitmap) data;
      image.setImageBitmap(photo);
      return true;
    }
    return false;
  }
});

Voici à quoi ressemble la méthode onCreateView entière :

La méthode onCreateView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
  View rootView = inflater.inflate(R.layout.fragment_main, container, false);

  final ListView list = (ListView) rootView.findViewById(android.R.id.list);
  final List<Map<String, Object>> contacts = retrieveContacts(getActivity().getContentResolver());

  if (contacts != null)
  {
    final SimpleAdapter adapter = new SimpleAdapter(getActivity(), contacts, R.layout.contact, new String[] { "name", "photo" }, new int[] { R.id.name,
        R.id.photo });
    adapter.setViewBinder(new ViewBinder()
    {

      @Override
      public boolean setViewValue(View view, Object data, String textRepresentation)
      {
        if ((view instanceof ImageView) & (data instanceof Bitmap))
        {
          final ImageView image = (ImageView) view;
          final Bitmap photo = (Bitmap) data;
          image.setImageBitmap(photo);
          return true;
        }
        return false;
      }
    });

    list.setAdapter(adapter);
  }

  return rootView;
}

Si vous exécutez l’application, vous devriez alors voir quelque chose comme ça à l’écran de votre téléphone :

Comme vous pouvez le constater, les photos des contacts qui en ont une sont bien affichées à l’écran !

A lire aussi…

Comments