lundi 12 décembre 2011

ListView : gestion de la sélection d'un item

Dans cette troisième partie sur les ListView, nous allons nous intéresser à la gestion du "click" sur un élément de la liste. 


Classiquement, la réponse à un évènement est gérée par un EventHandler. Ici, pour le click sur un élément d'une ListView, c'est l'évènement ListView.ItemClick qui est mis à contribution.
Une recherche rapide dans Google nous donne un exemple de code qui ressemble à ça :


protected override void OnCreate (Bundle bundle)
{

     // ...

     maListView = FindViewById<ListView>(Resource.Id.maListView);
     maListView.ItemClick+=new EventHandler<ItemEventArgs>(maListViewItemClick);
     //...
}



void maListViewItemClick(object sender, ItemEventArgs e)
{
      maListView = FindViewById<ListView>(Resource.Id.maListView);
      var selectedItem = maListView .Adapter.GetItem(e.Position);
}


Explication :
L'évenement levé indique, dans le paramètre ItemEventArgs, la position dans la liste de l'élément clické. En se référant à l'adapter de la listView, on peut ainsi récupérer l'objet affiché par l'item de la ListView, en utilisant la méthode Adapter.GetItem(int position)


Ouais, bof, ce n'est pas franchement propre comme façon de faire. Il suffit que, pour une raison ou pour une autre les données de l'adapter ne soient plus dans le même ordre que les données affichées à l'écran pour que la méthode nous produise un joli bug bien sympathique. Et même, si aujourd'hui, l'ordre est censé être le même pour l'adapter et la listview, personne ne peut dire qu'une prochaine version de ListView ne sera pas capable de faire des tris sans modifier les données de l'adapter... 


Ce qu'il faudrait, c'est pouvoir retrouver l'objet affiché dans l'item de la listview directement à partir de l'item lui-même. 
La documentation fait mention, au niveau de ListView, d'une propriété ListView.SelectedItem. Bien ! Ca semble être ce que l'on cherche. On récupère directement l'objet, et c'est bon. En pratique, ça ne fonctionne pas, le SelectedItem restant toujours null à l'exécution (j'ai posté un message dans le forum monodroid, sans obtenir de réponse satisfaisante à ce sujet...).


Il faut donc trouver une autre solution, et c'est l'item de la ListView qui va nous la donner :
Chaque élément d'une ListView est lui-même de type Android.Views.View. Et cette classe propose une propriété spécialement faite pour résoudre notre problème : View.Tag


View.Tag, de type Java.Lang.Object, permet, selon la doc Monodroid, de stocker tout objet : "Tags can also be used to store data within a view without resorting to another data structure.", ref ici
C'est pile poil ce que l'on veut faire !!!


La seule restriction est que l'objet que l'on va mettre dans ce Tag soit un Java.Lang.Object. Ce qui n'est pas bien compliqué : dans la plupart des cas, il suffit de faire hériter notre objet directement de Java.Lang.Object, 


Notre méthode de gestion du click devient donc la suivante : 



void maListViewItemClick(object sender, ItemEventArgs e)
{
     // On peut remplacer le type de selectedItem par un type perso,
     var selectedItem = e.View.Tag;
}





La dernière chose à ne pas oublier, est la valorisation du Tag, dans la méthode GetView de notre Adapter, en castant notre objet en Java.Lang.Object.




Si je reprends le code de l'article précédent sur les ListView, le GetView devient donc : 




public override View GetView (int position, View convertView, ViewGroup parent) 
{
     View v = convertView;
     if(position>=this.Count)
          return v;


     T o = this.items[position];
     if (o != null) 
     {                    
          if (v == null) 
          {
               LayoutInflater vi =   (LayoutInflater)Context.GetSystemService(Context.LayoutInflaterService);
               v = vi.Inflate(o.ResourceId, null);
          }
          o.FillView(v);
          // On valorise notre propriété Tag, ici : 
          v.Tag = (Java.Lang.Object)o;
     }
     return v;
}




Les autres modifications dans l'article précédent concerne la classe "Moto" : 
 public class Moto : Java.Lang.Object , iViewItem


et bien sur, l'ajout de la gestion de l'évènement ItemClick :


protected override void OnCreate (Bundle bundle)
{

[...]
 // Settings ListView
 ListView listOfMotorCycle = FindViewById<ListView>(Resource.Id.listMotorCycle);

  // On s'abonne à l'évement ItemClick 
listOfMotorCycle.ItemClick += new EventHandler<ItemEventArgs>(listOfMotorCycleItemClick);
[...]
}


La gestion de l'évènement en elle même 



void listOfMotorCycleItemClick(object sender, ItemEventArgs e)
{
     Moto selectedMoto = (Moto)e.View.Tag;
     // Et maintenant, je fais ce que je veux avec la moto sélectionnée...
     selectedMoto.DoWhatYouWant();
}



Si vous souhaitez l'intégralité du code, un petit message dans les commentaires...







Aucun commentaire:

Enregistrer un commentaire