Monthly Archives

2 Articles
AsyncTasks s’attendants

AsyncTasks s’attendants

Difficile de trouver un nom adéquat pour cet article.

En fait, je vais vous donner un conseil utile pour exécuter deux AsyncTasks en parallèle et qu’elles s’attendent pour continuer. Si vous ne le savez pas, les AsyncTasks sont des classes d’exécution de code en arrière-plan. Elles sont utilisées sur Android pour récupérer des données du WEB, faire des calculs, … et tout ça, sans gêner le Thread d’affichage.

De plus, vous pouvez effectuer des traitements sur le Thread d’affichage juste avant et juste après le traitement asynchrone très simplement. D’ailleurs la documentation Android privilégie l’utilisation des AsyncTasks en lieu et place des Threads.

Bon, revenons à nos moutons. Exécuter deux AsyncTasks en parallèle et attendre que les deux se terminent pour effectuer le traitement final. En fait, nous  allons lancer la seconde AsyncTasks dans le PreExecute() de la première et effectuer le traitement final dans le PostExecute de la première aussi. Nous attendrons que la seconde finisse dans le traitement asynchrone de la première afin d’éviter tout gel du Thread d’affichage.

Si vous m’avez suivi, voici le squelette java que nous devrions avoir :

private TaskA taskA;
private TaskB taskB;

class TaskA extends AsyncTask<Void, Void, Void>{

   @Override
   protected void onPreExecute(){
       // Ici, on lance notre seconde Task
       taskB = new TaskB();
       taskB.execute();
   }

   @Override
   protected Void doInBackground(Void... params){
       // Traitements asynchrones de la Task A
       // ...
       // Fin des traitements de la Task A

       // On attends le résultat de B
       try{
           taskB.get();
       } catch(ExecutionException e) {
           e.printStackTrace();
       } catch(InterruptedException e) {
           e.printStackTrace();
       }

       return null;
   }

   @Override
   protected void onPostExecute(Void result){
       // Le traitement final
       // Ici, vous êtes dans l'UI Thread
   }
}

class TaskB extends AsyncTask<Void, Void, Void>{

   @Override
   protected Void doInBackground(Void... params){
       // Traitements asynchrones de la Task B
       // ...
       // Fin des traitements de la Task B

       return null;
   }
}

La partie qui permet d’attendre que l’autre asynctask finisse est juste la fonction get() des AsyncTask. Elle évite de passer par les Threads et de faire le couple wait()/notify().

De plus elle lève certaines exceptions si jamais la tâche que vous attendez a été annulée ou a eu une erreur d’exécution entre autre. Et pour exécuter ces deux tâches vous avez juste à appeler cette ligne dans votre code :

taskA = new TaskA();
taskA.execute();

Bien entendu, vous pouvez étendre ce cas pour en effectuer X en parallèle. Je pense avoir fini ma partie conseil pour les AsyncTasks. Si vous avez des questions, suggestions, corrections, n’hésitez surtout pas à poster un commentaire pour que j’y réponde.

Gérer des évènements dans une listview.

Gérer des évènements dans une listview.

Bonjour à tous, ceci est mon premier tuto-conseil soyez indulgent :D.

Je vais vous présenter un adapter que j’utilise, assez souvent, dans Androïd. Tout d’abord voici une classe abstraite, qui n’est autre qu’un adapter qui étends BaseAdapter, et qui va nous aider a implémenter, facilement des événements dans notre liste. ClickableListAdapter.java

Je vois parfois des codes qui utilise, pour une liste, le onItemClickListener, donc l’action ne s’arrête qu’au simple click d’une ligne, alors que nous pourrions avoir plusieurs boutons, textview et autres interactivité sur un seul champ.

Personnellement voici ce que j’obtiens, sur une seule ligne :

[]1

La vue de chaque ligne avec leurs évènements propres.

J’ai donc deux actions, une checbkox et une image qui ouvre une quickaction, remplacé dans le tuto par un Toast. J’aurais pu rajouter plus, mais étant limité sur la largeur, au lieu de mettre plusieurs image-button, nous avons opté pour une quickaction qui est un semblant de pop-up.

Cela m’évite de retoucher ma vue et vu que nous rajoutions au fur et à mesure des actions possibles, c’était plus facile. Mais je m’égare, cela sera peut-être un prochain tuto, si vous me le demandez, revenons à nos adapter.

Explications:

Que fait la classe ClickableAdapter, c’est très simple, elle créé la vue de chaque ligne dans la liste, et nous la retourne. Tout ce que vous avez à faire, est d’étendre cette classe, et de définir votre classe Holder qui étendra ViewHolder. La classe Holder, n’est autre que les champs (textview, imageview, checkbox) que vous avez dans votre vue xml, qui défini chaque ligne. Nous créons notre Adapter personnel qui étends ClickableAdapter et nous nous retrouvons avec deux méthodes et un constructeur.

class MyAdapter extends ClickableListAdapter {

    public MyAdapter(Context context, int viewid, List objects) {
        super(context, viewid, objects);
        // TODO Auto−generated constructor stub
    }

    @Override
    protected ViewHolder createHolder(View v) {
        // TODO Auto−generated method stub
        return null;
    }

    @Override
    protected void bindHolder(ViewHolder h) {
        // TODO Auto−generated method stub

    }
}

Je récupère la taille de l’objet passé en paramètre, pour créer un tableau de boolean. Pourquoi me direz-vous, tout simplement parce que j’ai trouvé des petits problèmes de récupération de position dans une liste où nous devons scroller.

Il check des checkbox aléatoirement à la création/recréation de ligne et c’est très embêtant. J’ai trouvé comme solution, de créer un tableau de boolean, qui récupère la position de l’objet de la ligne (un int position dans l’objet bean lui même) et je check alors s’il est à vrai ou faux. À l’action check, il défini la position du tableau à vrai, et donc quand il recréera la ligne, si vous avez scrollé, il redéfinira correctement à vrai le checkbox.

int nbre = objects.size();
checkbox = new boolean[nbre];
for (int i = 0; i < nbre; i++) {
    checkbox[i] = false;
}

La méthode bindHolder, n’est que le remplissage de donnée, dans la vue elle-même. En conséquent, la méthode createHolder, nous créé la vue. Vu qu’elle est créé dynamiquement, toutes les actions seront les mêmes pour tous les lignes, il suffirait de rajouter des if ou un switch qui détecterait la position de l’objet dans la vue, avec la variable position, pour définir des actions différentes dans les évènements.

J’ai rajouté dans le onCheck la petite subtilité qui gère notre tableau de booleans, ainsi qu’une légère animation sur la ligne elle-même.

@Override
 public void onCheck(CompoundButton buttonView, boolean isChecked,
 ViewHolder mViewHolder) {
     RowHolder rh = (RowHolder) mViewHolder;
     br = (BeanRow) rh.data;

     checkbox[br.pos] = buttonView.isChecked();

     if (isChecked) {

         rh.rowAction.setEnabled(!checkbox[br.pos]);
         AlphaAnimation anim = new AlphaAnimation(1, 0.2f);
         anim.setDuration(0);
         anim.setFillAfter(true);
         v.startAnimation(anim);
     } else {

         rh.rowAction.setEnabled(!checkbox[br.pos]);
         AlphaAnimation anim = new AlphaAnimation(0.2f, 1);
         anim.setDuration(0);
         anim.setFillAfter(true);
         v.startAnimation(anim);
     }
}

N’oubliez pas que les onClick/onCheck et onLongClick sont ceux de la classe ClickableListAdapter, et non pas ceux proposé par Androïd, sinon vous ne récupéreriez pas la vue de votre ligne et donc, vous aurez une erreur. Voilà, j’espère avoir été clair. Je vous ai uploadé sur code.google le petit projet tuto, il sera surement plus facile a comprendre :-).

Bonne journée.