juil
21
2010

[Tutoriel Android] Partie 6 – Les ListView

Android et les listes

Bonjour à tous et bienvenue dans ce nouveau tutoriel qui aura pour sujet les ListView, sujet très fâcheux pour les novices du monde d’Android.

Tout d’abord, je vais vous expliquer le cheminement des données pour les ListView.

Comme son nom l’indique, une ListView est une liste de vue comprenant des items ; pour afficher une liste d’item dans celle-ci, il lui faut un adaptateur de données.

Comme chaque donnée est différente selon l’application, vous devez faire un adaptateur personnalisé pour entreposer celle-ci et l’afficher.

Je vais vous montrer un exemple basique d’une ListView avec un adaptateur nommé ArrayAdapter fourni avec Android.

Tout d’abord, on crée un nouveau projet avec pour seul information dans un layout principal, une ListView avec un id et sa taille ; comme-ceci :

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<ListView

android:id="@+id/lvListe"

android:layout_width="fill_parent"

android:layout_height="fill_parentA">

</ListView>

</LinearLayout>

Puis dans notre activité principale, nous allons récupérer notre ListView et lui affecter un adaptateur grâce à la méthode setAdapter. Ajoutons-y un objet de type ArrayAdapter, contenant une liste de String (pas les sous-vêtements !) avec un layout générique d’item que fournit la plateforme par défaut et enfin la liste des données.

public class MonActivite extends Activity {

ListView lvListe;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

lvListe = (ListView)findViewById(R.id.lvListe);

String[] listeStrings = {"France","Allemagne","Russie"};

lvListe.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,listeStrings));

}

}

ArrayAdapter<Type de données>(le contexte,le style des items,les données);

Rien de plus simple, lancez l’application et voyez pas vous-même.

Mais le problème se corse un petit peu si l’on désire ajouter nos propres données, ce qui sera a 99.9% le cas :lol:

Imaginons que nous allons faire une bibliothèque avec des livres, je ne vais pas vous refaire un cour de JAVA sur les objets, donc on va directement créer ce qui nous faut pour cette bibliothèque.

La bibliothèque sera définie comme une liste de type List.

Puis, nous allons créer un objet de type livre comprenant un titre et un auteur. Par la suite, vous pouvez étendre les possibilités avec un ISBN, etc… et afficher les informations lorsque l’on clique dessus (tiens ça me rappel un tutoriel où j’ai déjà écrit ça :cheerful:  ).

Voici la composition du type Livre :

public class Livre {

private String titre;

private String auteur;

public Livre(String titre, String auteur) {

this.titre = titre;

this.auteur = auteur;

}

public String getTitre() {

return titre;

}

public void setTitre(String titre) {

this.titre = titre;

}

public String getAuteur() {

return auteur;

}

public void setAuteur(String auteur) {

this.auteur = auteur;

}

}

Comme vous le voyez, on reste dans le B.A-BA de l’objet classique.

Puis dans notre activité, on crée notre bibliothèque.

List<Livre> maBibliotheque = new ArrayList<Livre>();

Puis, créons une fonction se nommant RemplirLaBibliotheque();

private void RemplirLaBibliotheque() {

maBibliotheque.clear();

maBibliotheque.add(new Livre("Starcraft 2 : Les diables du ciel", "William-C Dietz"));

maBibliotheque.add(new Livre("L'art du développement Android", "Mark Murphy"));

maBibliotheque.add(new Livre("Le seuil des ténèbres", "Karen Chance"));

}

J’ai ajouté trois livres aléatoires trouvés sur Amazon, mais vous pouvez en prendre d’autres, je ne serais pas vexé pour autant :lol:

Maintenant, nous allons créer notre propre Adaptateur pour gérer ces données. On va commencer par créer un layout qui formera notre item comme ceci :

Création itemlivre.xml

Puis dans le fichier XML, on rajoutera 2 textview avec respectivement un id Titre et un id Auteur.

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="wrap_content">

<TextView

android:id="@+id/tvTitre"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

/>

<TextView

android:id="@+id/tvAuteur"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

/>

</LinearLayout>

C’est simple mais efficace ! Nous voilà avec un gabarit correct pour travailler, maintenant on va se pencher sur notre adaptateur personnalisé.

Créez une classe nommée LivreAdapter, héritant de la classe BaseAdapter.

Création de classe LivreAdapter

Voilà, notre classe est générée et on va pouvoir la modifier comme nous le voulons.

Le générateur de classes à rajouter directement les méthodes à remplir pour le bon fonctionnement de l’adaptateur, comme ça nous n’aurons pas de temps à perdre (merci Eclipse).

On retrouvera ces méthodes :

getCount() qui retournera le nombre d’éléments dans notre liste.

getItem() qui retournera notre objet livre à la position indiquée.

getItemId() qui retournera l’id du livre.

getView() qui retournera la vue de l’item pour l’affichage.

Rajoutons un type List<Livre> bibilo, dans notre classe et créons un constructeur par défaut prenant une liste et un contexte en paramètres.

Rajoutons aussi à notre classe un LayoutInflater, qui aura pour mission de charger notre fichier XML pour l’item, c’est là que l’on utilisatera le contexte.

List<Livre> biblio;

LayoutInflater inflater;

public LivreAdapter(Context context,List<Livre> biblio) {

inflater = LayoutInflater.from(context);

this.biblio = biblio;

}

Vous suivez toujours ? Oui alors on continue !

Remplissons nos différentes méthodes, tout d’abord getCount qui retournera la taille de la bibliothèque.

@Override

public int getCount() {

return biblio.size();

}

Ensuite, la méthode getItem qui retournera l’item.

@Override

public Object getItem(int position) {

return biblio.get(position);

}

Et la troisième, qui sera getItemId qui retournera la position de l’item.

@Override

public long getItemId(int position) {

return position;

}

Maintenant la dernière, qui est la plus difficile au début mais vraiment au début ^^

Créons une classe juste avant cette méthode (oui on peut le faire en JAVA une classe dans une classe !) qui sera nommée ViewHolder.

Elle nous servira à mémoriser les éléments de la liste en mémoire pour qu’à chaque rafraichissement l’écran ne scintille pas (c’est un genre de buffer comme en graphisme).

private class ViewHolder {

TextView tvTitre;

TextView tvAuteur;

}

Dans la méthode, nous allons utiliser un ViewHolder, puis nous allons vérifier que la view présente n’est pas null sinon nous allons la créer, et ensuite charger l’XML en mémoire pour l’attribuer à notre objet.

Et enfin tagguer notre objet pour pouvoir le récupérer à la prochaine mise à jour graphique.

Et pour finir, on y attribue les données et on retourne la vue.

@Override

public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder holder;

if(convertView == null) {

holder = new ViewHolder();

convertView = inflater.inflate(R.layout.itemlivre, null);

holder.tvTitre = (TextView)convertView.findViewById(R.id.tvTitre);

holder.tvAuteur = (TextView)convertView.findViewById(R.id.tvAuteur);

convertView.setTag(holder);

} else {

holder = (ViewHolder) convertView.getTag();

}

holder.tvTitre.setText(biblio.get(position).getTitre());

holder.tvAuteur.setText(biblio.get(position).getAuteur());

return convertView;

}

Voilà notre fonction terminée !

Retournons dans notre activité principale et modifions notre code pour que la liste prenne en paramètres notre LivreAdapter comme ceci :

LivreAdapter adapter = new LivreAdapter(this, maBibliotheque);

lvListe.setAdapter(adapter);

Lancez l’application et voyez par vous même !

Ecran final

Si vos données changent, penser à utiliser la fonction adapter.notifyDataSetChanged(); qui aura pour effet de notifier le changement de données et de recharger la liste automatiquement.

J’espère que ce tutoriel vous aura plus et je vous dis à la prochaine :wink:

Voici les sources : FormationListeAdapter

Download PDF
  • http://SiteWeb manu

    Bonjour et bravo pour ce tuto très clair et efficace !

    Il manque juste l’appel à RemplirLaBibliotheque() (mais c’est évident…).

    encore bravo et bon courage pour d’autres tutos !

  • http://SiteWeb dom

    Bonjour j’essaie l’exemple au sein d’un test.
    Mais à chaque fois que je change la propriété android:id de mon ListView dans la déclaration XML j’ai une erreur : L’application se ferme.

    Par défaut j’ai @android:id/list que je remplace par @+id/lvListe.

    Une idée ?
    Et merci encore pour cet exemple

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Tu pourrais nous copier / coller ton code XML s’il te plait ?

  • http://SiteWeb dom

    Je pense avoir compris ;)
    Mon activité est une activité typée ListActivity et je pense que dans ce cas précis, il faut obligatoirement un élément listView dans le layout ayant pour id @android:id/list.

    Je pense que la différence vient du fait que tu utilises une activité Activity.

    En tout cas merci pour ce tuto ;)

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Effectivement en utilisant une ListActivity le code est différent.

  • http://SiteWeb Maxime

    Moi j’ai un problème.
    C’est au niveau de l’appel de RemplirLaBibliotheque, ça ne marche pas… Quelqu’un pourrai me le montrer.

    Sinon, dès que l’appli se lance, ça plante.

    J’ai fait :
    LivreAdapter adapter = new LivreAdapter(this, maBibliotheque);
    RemplirLaBibliotheque();
    lvListe.setAdapter(adapter);

    Et si je mais en commentaire la troisième ligne, forcément ça ne plante pas, mais à ce moment, ma liste est vide… :cwy:
    Quelqu’un pourrai m’aider ?

    Merkiki

    PS : Je pense qu’il vaudrai peut être mieux que tu m’envoie les sources à lepotoman [at] msn . com parce que je n’arrive vraiment pas à débugger…

    Merci

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Voila j’ai rajouté les sources dans l’article à la fin :biggrin:

  • http://SiteWeb kikizz

    SUPER TUTO, tu aurais pus t’arrêter sur un simple remplissage de Listeview mais tu as été plus loin, j’ai pu résoudre ma problématique simplement, donc un grand merci.
    Je m’en vais lire tous les autres tutos. :smile: :biggrin:

  • http://sofiennerida.blogspot.com Sofienne

    Merci, merci, merci , merci , merci !!!

    J’ai dit merci ? ;)

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    En effet vous l’avez dit ;)

  • http://android.cyrilmottier.com Cyril Mottier

    Tutorial intéressant mais composés de quelques petits problèmes …

    - Le layout comprend une dans un … le est en fait inutile puisqu’il ne contient qu’un seul et unique élément
    - L’élément dispose de l’attribut android:layout_height=”wrap_content” … c’est bien sûr possible mais fortement déconseillé car cela implique que la ListView doit mesurer l’ensemble des éléments avant de pouvoir s’afficher. Dans le cas d’une ListView a 3 élément ça va mais si on a 10000 éléments, ça devient plus que problématique. Il est donc conseillé de mettre android:layout_height=”fill_parent”.
    - Android s’écrit “Android” et pas “Androïd” :p
    - Pour améliorer tout ça .. autant utiliser une ListActivity ;)

    Encore bravo pour ces tutos.

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Salut Cyril,

    Merci pour le coup du wrap_content je ne savais pas dans le cas du listview, merci de le préciser :)

    Pour le reste je vais corriger ça, et bravo à toi aussi pour tes tutoriaux et ton p’tit Greendroid

  • http://SiteWeb Gg

    En tant que néophyte j’ai été ravi de découvrir que l’on pouvait associer une structure de donnée libre à n’importe quel View (via la méthode setTag) mais son utilisation dans ce tutoriel a un peu freiné ma compréhension de l’Adapter. Or le tag n’était pas du tout nécessaire ! Si l’on ne l’utilise pas alors la méthode getView overridée ne contient plus que trois lignes (plus le return) :
    if (convertView == null) convertView = inflater.inflate(R.layout.item_livre, null);
    ((TextView)convertView.findViewById(R.id.tvTitre)).setText(biblio.get(position).getTitre());
    ((TextView)convertView.findViewById(R.id.tvAuteur)).setText(biblio.get(position).getAuteur());

    Ca me semble plus lisible ainsi…

    Merci pour cet excellent tutoriel ! (un de plus)

  • http://SiteWeb cadavor

    Bonjour,

    J’ai un gros soucis avec une listview que j’ai implementé à partir de ce tuto (très bien fait et très instructif merci!!!).
    En Run, la liste n’est pas rempli à la création (aucun getView d’appelé). Si je la rafraichit manuellement, j’obtiens un beau crash!!!
    01-24 22:48:54.856: INFO/System.out(221): getView 0 of 14
    01-24 22:48:55.056: INFO/System.out(221): getView 1 of 14
    01-24 22:48:55.316: INFO/System.out(221): getView 2 of 14
    01-24 22:48:55.426: INFO/System.out(221): getView 3 of 14
    01-24 22:48:55.526: INFO/System.out(221): getView 4 of 14
    01-24 22:48:55.626: INFO/System.out(221): getView 5 of 14
    01-24 22:48:55.716: INFO/System.out(221): getView 6 of 14
    01-24 22:48:55.826: INFO/System.out(221): getView 7 of 14
    01-24 22:48:55.926: INFO/System.out(221): getView 8 of 14
    01-24 22:48:56.026: INFO/System.out(221): getView 9 of 14
    01-24 22:48:56.126: INFO/System.out(221): getView 10 of 0
    01-24 22:48:56.296: ERROR/AndroidRuntime(221): java.lang.IndexOutOfBoundsException: Invalid location 10, size is 0
    01-24 22:48:56.296: ERROR/AndroidRuntime(221): at java.util.ArrayList.get(ArrayList.java:353)
    01-24 22:48:56.296: ERROR/AndroidRuntime(221): at android.francehockey.app.ResultAdapter.getView(ResultAdapter.java:72)

    Le System.out est le suivant dans l’override getView de l’adapter :
    System.out.println(“getView ” + position + ” of ” + ListResult.size());

    Je me suis rendu compte en debugguant un petit peu (3 jours) que le ArrayList n’était pas mis à jour de suite, il lui fallait un certain temps après les appel .add pour mettre à jour le contenu. En plaçant un simple breakpoint avant le notifyDataSetChanged() cela pme permet d’avoir l’affichage dans la listView.

    Je ne comprends vraiment pas le problème et encore moins comment le résoudre.
    Si quelqu’un veut bien m’aider…

  • http://SiteWeb cadavor

    En continuant le debug je me suis rendu que l’ajout d’une progressBar et donc d’un thread entrainé ce problème de retard!!! En le supprimant le problème n’est plus présent mais la progressBar non plus… -_-

    Comment avoir les 2?

  • http://SiteWeb cadavor

    En continuant le debug je me suis rendu que l’ajout d’une progressBar et donc d’un thread entrainait ce problème de retard!!! En le supprimant le problème n’est plus présent mais la progressBar non plus… -_-

    Comment avoir les 2?

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Un thread traitant les données, une progressbar dans un progress dialog pour eviter que l’utilisateur bricole pendant ce temps et voilà.

  • http://SiteWeb Tomny

    Bonjour,
    j’ai bien appliquer ce tuto qui fonctionne très bien.
    J’ai voulu l’étendre en utilisant la mise à jour des données et cela fonctionne à un peu détail près, ma liste se répète à chaque modification (elle se retrouve en dessous de la version précédente).
    Je ne comprends pas trop pourquoi. :dizzy:

    Pour info j’ai passer mon adapter en singleton car je dois le modifier depuis plusieurs autres classes ou activity de mon application.

    Merci encore.

  • Imene Sbiaat

    I like it

  • Pingback: Ace Art | [Tutoriel Android] Partie 15 – Expandable List

  • Akiat

    Re bonjour à tous.

    Me voila encore confronté à un problème, que je n’arrive pas à régler…

    En fait il s’agit du Ressource Manager “R”.

    R cannot be resolved to a variable.

    Le fait est que si je met un “import android.R;” ce n’est plus le R qui n’est pas reconnu, mais le “main” de R.id.main (pareil pour lvListe). ce qui est normal car il cherche dans le Ressource Manager du package android…

    Mais l’auto-complete sur un “import com.MyListView” me le complete directement par “import com.MyListView.*;” . Et si j’essai juste d’importer import com.MyListView.R; il ne le trouve pas…

    Je ne comprends vraiment pas.

    J’espère que quelqu’un pourra m’aider…

    Merci par avance.

  • Akiat

    Le problème viens en fait du fait que Eclipse ne veut pas me générer de R.java…

    Malgré les Clean, build, ajout de layouts, modification d’un xml…

    La y’a vraiment un truc qui m’échappe…

  • http://www.toutestnet.fr/ Maxime

    Encore un bon tuto qui me permet d’avancer progressivement vers mes objectifs.

    Juste un petit détail : Le premier code, correspondant au layout principal, Pour la ListView, vous avez mis :
    android:layout_height=”fill_parentA”

    Le A me semble de trop, non ?

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Bonjour Maxime,

    Effectivement une petite coquille c’est glissée dans l’article ;)

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    le R.java ce trouve dans les imports en haut de ta page, supprime l’import contenant R. normalement il va t’offrir de nouveau la bonne possibilité

  • Akiat

    Merci pour ta réponse, cependant, la seule chose qui a fonctionné à été de recréer un nouveau projet… Bizarre…

  • shibakaneki

    Concernant le problème du R.java qui n’est plus généré. Est-ce que par hasard tu n’aurais pas supprimé le répertoire ‘gen’ ? C’était mon cas et j’avais le même problème. En le recréant manuellement et en faisant un Refresh + rebuild du projet le fichier R.java a été regénéré.

    Pas beosin d’ajouter l’import explicitement.

  • Sickly

    Merci pour ce tuto.
    Par contre il y a un problème dès lors que l’on a des entier pour cette ligne:

    holder.tvAuteur.setText(biblio.get(position).getAuteur());

    Sinon sa fonctionne niquel :)

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Bonjour Sickly,

    Regarde dans ton logcat l’erreur qui est levée elle t’informera rapidement du problème ;)

  • Thomy

    As tu une idée si l’on peut avoir une ArrayList dans ta classe Livre (pour gérer des commentaires sur un livre, plusieurs auteurs…) ?

  • http://www.ace-art.fr/wordpress Acesyde

    Bien entendue, on peut faire ce que l’on veut ;)

    Après c’est plus un problème de modélisation du concept qu’on problème de développement.

  • Thomy

    OK,  parce que je suis en train d’essayé cela justement et je ne sais pas si je procède de la bonne façon.En fait, dans l’itemLivre.xml, j’ai à nouveau une ListView qui correspond à un champ ArrayList de ma classe Livre. Je procède de la même façon pour cette nouvelle ListView avec un adapter que j’appelle cette fois ci dans LivreAdapter pour pouvoir afficher ces données dans mon appli mais j’ai qu’un seul item qui apparaît. Donc je me demandais si je procédais de la bonne façon

  • http://www.ace-art.fr/wordpress Acesyde

    Le mieux serait d’avoir une activité affichant la liste des livres, quand tu clic sur le livre tu ouvres une nouvelle activités avec la liste des commentaires.

    C’est simple à maintenir et la navigation est fluide.

    Imaginons le cas ou tu charges les commentaires lorsque tu clics sur le livre et non au démarrage de l’application, sinon ça prend trop de mémoire.

    Tu as toutes les cartes en mains désormais

  • Dacaramel

    Tres bon tuto! merci!
    j’ai essayé vos codes en ajoutant les icones avec listview mais en vain (je suis encore noob!!). Avez-vous un example avec listview +icon.
     Si vous pouvez me donner quelques pistes sur ce que j’ai fait. Ca serait super cool.

    dans :public class Livre{} j’ai aujouté:
    public class Livre{
        private ImageViw image;
        .
        …….
        public Livre( ImageView image,String titre, String auteur) {
            this.image = image;
            this.titre = titre;
            this.auteur = auteur;
        }
        public String getImage() {
            return image;
        }
        public String setImage() {
            this.image=image;
        }
        …..
        }
       
    ***********LivreAdapter**********************
    private class ViewHolder {
           
            ImageView image;
            TextView titre;
            TextView auteur;
        }
        …..
        ..
        .
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder holder;

        if(convertView == null) {
            holder = new ViewHolder();
            convertView = inflater.inflate(R.layout.itemlivre, null);
            holder.tvTitre = (TextView)convertView.findViewById(R.id.tvTitre);
            holder.tvAuteur = (TextView)convertView.findViewById(R.id.tvAuteur);
            //code aujoute
            holder.image= (ImageView) convertView.findViewById(R.id.image);
            convertView.setTag(holder);
        } else {
        holder = (ViewHolder) convertView.getTag();
        }
       
       
        //holder.image.setText(biblio.get(position).getImage()); //bug pb de syntaxe
        holder.tvTitre.setText(biblio.get(position).getTitre());
        holder.tvAuteur.setText(biblio.get(position).getAuteur());

        return convertView;
    }   

    *************RemplirLaBibliotheque***********************************

    comment aujouter dans “maBibliotheque.add” avec 3 argument?

    maBibliotheque.add(new Livre(???????,”Le seuil des ténèbres”, “Karen Chance”));

  • http://www.ace-art.fr/wordpress Acesyde

    Tu as déjà tout ce qu’il te faut je vois.

    en faites c’est pas maBibliotheque.add qui prend 3 args mais la classe Livre d’ou le new Livre(arg1,arg2,arg3) je vois qu’en plus tu as déjà implémenté les 3 arguments dans le constructeur de la classe Livre.

    En suite pour l’image tout est correctement fait dans ton code il reste juste à rajouter un imageview dans ton template xml de ton item.
    Ensuite le tour est joué. J’espère avoir été assez clair sinon fait moi signe :)

Switch to mobile version