1. Introduction

Lors d'un développement pour Android, j'ai eu besoin d'un objet graphique et les objets graphiques proposés par le SDK Android ne me convenaient pas.

J'avais besoin d'un indicateur de position afin d'aider l'utilisateur à se repérer lors de sa navigation dans les différents écrans et les seuls objets tout faits et disponibles les plus proches étaient la « scroll bar » ou la « progress bar ». Outre le fait que le graphisme ne me plaisait pas, ces objets ne répondaient pas à mon besoin qui était, comme sur une carte plane de la terre, lorsque l'on va continuellement vers la droite, on revient par la gauche, « MAX + 1 = MIN » et « MIN -1 = MAX », cet indicateur de position devait pouvoir « tourner ».

J'ai donc commencé à regarder comment créer de toutes pièces un tel objet et ce tutoriel est le résultat de cette recherche.

Cet article est organisé comme suit :

  • le paragraphe 2 spécifie mon besoin fonctionnel ;
  • le paragraphe 3 décrit l'implémentation de base ;
  • le paragraphe 4 décrit un projet de test qui utilise cet objet graphique ;
  • le paragraphe 5 laisse libre cours aux graphistes afin qu'ils expriment leur créativité ;
  • et enfin, le chapitre 6 se propose de conclure cet article.

2. Description du besoin fonctionnel

Dans le programme e-verbe pour Android, il y a un champ texte qui affiche le temps conjugué.

Lorsque l'utilisateur effectue un balayage horizontal sur ce champ texte, cela permet de changer le temps de la conjugaison (présent, imparfait, passé simple, futur simple…).

Lorsque l'utilisateur effectue un balayage vertical sur ce champ texte, cela permet de changer le mode de la conjugaison (indicatif, subjonctif, impératif, conditionnel…).

De plus, lorsque l'utilisateur est au dernier temps (ou mode) et qu'il continue son balayage vers la droite (ou vers le bas), il doit revenir au premier temps (ou mode).

De même, lorsque l'utilisateur est au premier temps (ou mode) et qu'il continue son balayage vers la gauche (ou vers le haut), il doit passer au dernier temps (ou mode).

Le besoin est donc de créer un indicateur de position qui permette à l'utilisateur de se repérer dans ses balayages.

Image non disponible

De plus, tous les modes de conjugaison n'ont pas le même nombre de temps (huit temps pour l'indicatif et deux temps pour l'impératif par exemple). Il faut donc un indicateur de position qui puisse se reconfigurer de manière dynamique.

Il y a bien les objets « scroll bar » ou « progress bar »qui existent dans le SDK Android mais ils ont plus une notion de « restant à parcourir » par rapport à « déjà parcouru » qu'une notion de « position courante » parmi un « ensemble de positions ».

Et en plus, ils répondaient difficilement à mon besoin de faire tourner ma position « MAX + 1 = MIN » et « MIN -1 = MAX ».

La copie d'écran ci-dessous montre le résultat obtenu :

Image non disponible

Il y a deux approches possibles de cette problématique :

  • l'indicateur de position est un objet actif sur lequel l'utilisateur peut agir. C'est donc l'indicateur de position qui est activé par l'utilisateur et c'est cet indicateur de position qui va demander à la vue de se rafraîchir en fonction de la position courante de l'indicateur de position ;
  • l'indicateur de position est un objet passif sur lequel l'utilisateur ne peut pas agir. C'est la vue qui est activée par l'utilisateur et qui demande à l'indicateur de position de refléter la position courante.

C'est cette deuxième approche que j'ai choisi d'utiliser.

3. L'implémentation de base

La classe de base de ce nouvel objet graphique est l'objet « GuiPositionIndicator » qui dérive de l'objet « View ».

Les fonctions et attributs implémentés par cette classe peuvent se classer en deux catégories (outre leur caractère privé, public ou protégé) :

  • ceux nécessaires aux fonctionnalités à implémenter. Les noms de ces fonctions sont préfixés par les caractères « Work » ;
  • ceux nécessaires et imposés par la classe « View ». Les noms de ces fonctions ne sont pas préfixés car leurs noms sont imposés par la classe « View » ;
  • les attributs (variables membres) sont en plus préfixés par les caractères « m_ ».

3.1. Les fonctions et attributs « Work »

Les fonctions et attributs « Work » à implémenter sont :

Nom Rôle
private void WorkInternalInit() Exécute l'initialisation des membres de la classe. Cette fonction est appelée par les constructeurs
public void WorkRolloverYes() Positionne le mode rollover (MAX + 1 = MIN et MIN - 1 = MAX)
public void WorkRolloverNo() Annule le mode rollover (MAX + 1 = MAX et MIN - 1 = MIN)
public final boolean WorkRollover() Indique si le mode rollover est positionné ou non
private boolean m_WorkRollover Variable mémorisant le statut du mode rollover
public void WorkSize(int Size) Fonction permettant d'indiquer le nombre de positions possibles
public final int WorkSize() Fonction retournant le nombre de positions possibles
private int m_WorkSize Variable mémorisant le nombre de positions possibles
public void WorkPos(int Pos) Fonction permettant d'indiquer la position courante
public final int WorkPos() Fonction permettant de récupérer la position courante
private int m_WorkPos Variable mémorisant la position courante
public void WorkPosNext() Fonction permettant d'avancer d'une position la position courante
public void WorkPosPrevious() Fonction permettant de reculer d'une position la position courante

3.2. Les fonctions et attributs « View »

Les fonctions et attributs « View » à implémenter sont :

Nom Rôle
public GuiPositionIndicator(Context context) Constructeur simple pour créer l'objet à partir du code
public GuiPositionIndicator(Context context, AttributeSet attrs) Constructeur utilisé pour créer l'objet à partir d'un fichier XML de ressources
public GuiPositionIndicator(Context context, AttributeSet attrs, int defStyle) Constructeur utilisé pour créer l'objet à partir d'un fichier XML de ressources tout en appliquant un style spécifique
protected void onDraw(Canvas canvas) Fonction appelée par Android pour redessiner l'objet graphique

3.3. Le code complet de la classe « GuiIndicatorPosition »

Comme on peut le voir l'interface de la classe est très simple, il y a quelques fonctions nécessaires à la classe View. Les autres fonctions sont spécifiques à la fonctionnalité à implémenter.

Pour l'instant, le code de la fonction onDraw(), qui est en charge d'afficher l'objet graphique, est minimal. Il se contente de dessiner sa surface en vert et d'afficher la position courante par rapport au nombre de positions possibles. C'est dans cette fonction que les graphistes pourront s'exprimer.

Vous remarquerez aussi que l'on ne redessine pas l'objet graphique dans les fonctions « public void WorkSize(int Size) » et « public void WorkPos(int Pos) ». La vue est simplement invalidée par un appel à la fonction « invalidate') ». C'est le framework qui se chargera d'appeler la fonction « protected void onDraw(Canvas canvas) » dès que possible.

Le code de la classe « GuiIndicatorPosition » est le suivant :

 
CacherSélectionnez
  1. package fr.circitor.test; 
  2. import android.content.Context; 
  3. import android.graphics.Canvas; 
  4. import android.graphics.Color; 
  5. import android.graphics.Paint; 
  6. import android.util.AttributeSet; 
  7. import android.view.View; 
  8.   
  9. public class GuiPositionIndicator extends View 
  10. { 
  11.     // Constructeur simple pour créer l'objet à partir du code 
  12.     public GuiPositionIndicator(Context context) 
  13.     { 
  14.         super(context); 
  15.         WorkInternalInit(); 
  16.     } 
  17.   
  18.     // Constructeur utilisé pour créer l'objet à partir d'un fichier XML de ressources 
  19.     public GuiPositionIndicator(Context context, AttributeSet attrs) 
  20.     { 
  21.         super(context, attrs); 
  22.         WorkInternalInit(); 
  23.     } 
  24.   
  25.     // Constructeur utilisé pour créer l'objet à partir d'un fichier XML de ressources tout en appliquant un style spécifique 
  26.     public GuiPositionIndicator(Context context, AttributeSet attrs, int defStyle) 
  27.     { 
  28.         super(context, attrs, defStyle); 
  29.         WorkInternalInit(); 
  30.     } 
  31.   
  32.     // Fonction appelée par Android pour redessiner l'objet graphique 
  33.     protected void onDraw(Canvas canvas) 
  34.     { 
  35.         // crée une couleur verte 
  36.         Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 
  37.         paint.setColor(Color.GREEN); 
  38.         paint.setStyle(Paint.Style.FILL_AND_STROKE); 
  39.   
  40.         // dessine un rectangle 
  41.         canvas.drawRect(0, 0, getWidth(), getHeight(), paint); 
  42.   
  43.         // le texte à afficher 
  44.         String text = "Position=" + Integer.toString(WorkPos()) + ", Taille=" + Integer.toString(WorkSize()); 
  45.   
  46.         // la couleur du text 
  47.         paint.setColor(Color.BLACK); 
  48.   
  49.         // affichage du texte 
  50.         canvas.drawText(text, 10, getHeight() / 2, paint); 
  51.     } 
  52.   
  53.     // Execute l'initialisation des membres de la classe, appelée par le constructeur 
  54.     private void WorkInternalInit() 
  55.     { 
  56.         WorkSize(0); 
  57.         WorkPos(0); 
  58.         WorkRolloverYes(); 
  59.     } 
  60.   
  61.     // Positionne le mode rollover (MAX + 1 = MIN et MIN ? 1 = MAX) 
  62.     public void WorkRolloverYes() { m_WorkRollover = true; } 
  63.   
  64.     // Annule le mode rollover (MAX + 1 = MAX et MIN ? 1 = MIN) 
  65.     public void WorkRolloverNo() { m_WorkRollover = false; } 
  66.   
  67.     // Indique si le mode rollover est positionné ou non 
  68.     public final boolean WorkRollover() { return m_WorkRollover; } 
  69.   
  70.     // Variable mémorisant le statut du mode rollover 
  71.     private boolean m_WorkRollover; 
  72.   
  73.     // Fonction permettant d'indiquer le nombre de positions possibles 
  74.     public void WorkSize(int Size) 
  75.     { 
  76.         m_WorkSize = Size; 
  77.         invalidate(); 
  78.     } 
  79.   
  80.     // Fonction retournant le nombre de positions possibles 
  81.     public final int WorkSize() { return m_WorkSize; } 
  82.   
  83.     // Variable mémorisant le nombre de positions possibles 
  84.     private int m_WorkSize; 
  85.   
  86.     // Fonction permettant d'indiquer la position courante 
  87.     public void WorkPos(int Pos) 
  88.     { 
  89.         m_WorkPos = Pos; 
  90.         invalidate(); 
  91.     } 
  92.   
  93.     // Fonction permettant de récupérer la position courante 
  94.     public final int WorkPos() { return m_WorkPos; } 
  95.   
  96.     // Variable mémorisant la position courante 
  97.     private int m_WorkPos; 
  98.   
  99.     // Fonction permettant d'avancer d'une position la position courante 
  100.     public void WorkPosNext() 
  101.     { 
  102.         int current = WorkPos() + 1; 
  103.         if(current == WorkSize()) 
  104.         { 
  105.             if(WorkRollover() == false) current--; 
  106.             else current = 0; 
  107.         } 
  108.         WorkPos(current); 
  109.     } 
  110.   
  111.     // Fonction permettant de reculer d'une position la position courante 
  112.     public void WorkPosPrevious() 
  113.     { 
  114.         int current = WorkPos() - 1; 
  115.         if(current == -1) 
  116.         { 
  117.             if(WorkRollover() == false) current++; 
  118.             else current = WorkSize() - 1; 
  119.         } 
  120.         WorkPos(current); 
  121.     } 
  122. } 

3.4. La création d'un objet « GuiPositionIndicator »

L'objet « GuiPositionIndicator » est créé directement à partir du fichier XML de ressources comme ceci :

 
CacherSélectionnez
  1.     <view 
  2.         class="fr.circitor.test.GuiPositionIndicator"  
  3.         android:id="@+id/IdGuiPositionIndicator" 
  4.         android:layout_width="fill_parent" 
  5.         android:layout_height="40sp" 
  6.         /> 

C'est le nom de l'attribut « Class » qui fait le lien avec notre classe « GuiIndicatorPosition ».

3.5. Le code de l'activité

L'initialisation de la classe GuiPositionIndicator peut être faite dans le constructeur de l'activité :

 
CacherSélectionnez
  1. package fr.circitor.test; 
  2. import android.app.Activity; 
  3. import android.os.Bundle; 
  4. import android.view.View; 
  5. import android.widget.TextView; 
  6.   
  7. public class TestActivity extends Activity 
  8. { 
  9.     public void onCreate(Bundle savedInstanceState) 
  10.     { 
  11.         super.onCreate(savedInstanceState); 
  12.         setContentView(R.layout.main); 
  13.   
  14.         // ajoute un texte dans le TextView 
  15.         TextView tv = (TextView)findViewById(R.id.IdTextView); 
  16.         tv.setText("Cliquez-moi !!"); 
  17.   
  18.         // ajoute un handler de clic sur le TextView 
  19.         tv.setOnClickListener(m_handler); 
  20.   
  21.         // récupère l'indicateur de position 
  22.         m_ctrl = (GuiPositionIndicator)findViewById(R.id.IdGuiPositionIndicator); 
  23.   
  24.         // initialise sa taille et sa position 
  25.         m_ctrl.WorkSize(10); 
  26.         m_ctrl.WorkPos(4); 
  27.     } 
  28.   
  29.     private GuiPositionIndicator m_ctrl; 
  30.     private View.OnClickListener m_handler = new View.OnClickListener() 
  31.     { 
  32.         public void onClick(View v) 
  33.         { 
  34.             // sur un clic dans le text view, on fait avancer l'indicateur de position 
  35.             m_ctrl.WorkPosNext(); 
  36.         } 
  37.     }; 
  38. } 

Vous remarquerez que :

  • l'initialisation des valeurs du contrôle « GuiPositionIndicator » est faite de manière très simple dans le constructeur de l'activité ;
  • il y a un EventListener qui écoute les clics de l'utilisateur. À chaque clic sur le texte, la position courante est avancée.

4. Exemple d'un projet complet

Un projet complet utilisant l'objet graphique « GuiPositionIndicateur peut être téléchargé ici. Vous remarquerez qu'il ne contient aucune fioriture, c'est le projet le plus simple qui soit.

4.1. Fonctionnement de l'exemple

Le fonctionnement de l'exemple est assez simple.

Il y a deux objets graphiques :

  • une fenêtre de texte (un objet Android « textView ») en gris qui affiche la position courante ainsi que la taille de l'indicateur de position ;
  • l'indicateur de position (un objet « GuiPositionIndicator ») juste en dessous qui affiche sous forme graphique la position courante et la taille.
Image non disponible

4.2. L'objet texte

4.2.1. La déclaration

L'objet texte est déclaré directement dans les ressources du projet :

 
CacherSélectionnez
  1.     <TextView 
  2.         android:layout_width="fill_parent" 
  3.         android:layout_height="140sp" 
  4.         android:background="#FF555555" 
  5.         android:id="@+id/IdTextView" 
  6.         /> 

4.2.2. L'initialisation de l'objet texte

L'initialisation de l'objet texte dans la fonction onCreate() de l'activité est basique :

 
CacherSélectionnez
  1.         // ajoute un texte dans le TextView 
  2.         m_tv = (TextView)findViewById(R.id.IdTextView); 
  3.   
  4.         // ajoute un handler sur le TextView 
  5.         m_tv.setOnTouchListener(m_handler); 

Une fonction dédiée, la fonction RefreshText(), permet d'afficher le texte de la fenêtre ainsi que sa position courante :

 
CacherSélectionnez
  1.     private void RefreshText() 
  2.     { 
  3.         // affiche un texte ainsi que la position courante  
  4.         m_tv.setText("Faites un balayage droite/gauche !!\n\nPosition=" + m_ctrl.WorkPos() + "/" + m_ctrl.WorkSize()); 
  5.     } 

4.2.3. Le gestionnaire d'événements

La partie la plus intéressante de l'objet texte est probablement le gestionnaire d'événements qui permet la gestion du balayage :

 
CacherSélectionnez
  1.     // le handler d'événements de la fenêtre texte 
  2.     private View.OnTouchListener m_handler = new View.OnTouchListener() 
  3.     { 
  4.         public boolean onTouch(View v, MotionEvent event) 
  5.         { 
  6.             Log.i("onTouchEvent", "event=" + event.toString()); 
  7.             if(event.getX() > v.getWidth()) 
  8.                 return true; 
  9.             if(event.getY() > v.getHeight()) 
  10.                 return true; 
  11.   
  12.             if(event.getAction() == MotionEvent.ACTION_DOWN) 
  13.             { 
  14.                 Log.i("onTouchEvent", "DOWN"); 
  15.                 if(m_flag_swipe_is_on == true) 
  16.                     return true; 
  17.                 m_flag_swipe_is_on = true; 
  18.                 m_x_swipe = event.getX(); 
  19.                 return true; 
  20.             } 
  21.   
  22.             if(event.getAction() == MotionEvent.ACTION_MOVE) 
  23.             { 
  24.                 Log.i("onTouchEvent", "MOVE"); 
  25.                 if(m_flag_swipe_is_on == false) 
  26.                     return true; 
  27.   
  28.                 float delta_x = event.getX() - m_x_swipe; 
  29.                 Log.i("onTouch", "Move, dx=" + delta_x); 
  30.   
  31.                 // seul un déplacement plus grand que 10 est pris en compte 
  32.                 if(StrictMath.abs(delta_x) < m_move_sensibility_x) 
  33.                     return true; 
  34.                 if(delta_x > 0) 
  35.                     m_ctrl.WorkPosNext(); 
  36.                 else 
  37.                     m_ctrl.WorkPosPrevious(); 
  38.   
  39.                 m_x_swipe = event.getX(); 
  40.                 RefreshText(); 
  41.                 return true; 
  42.             } 
  43.   
  44.             if(event.getAction() == MotionEvent.ACTION_UP) 
  45.             { 
  46.                 Log.i("onTouchEvent", "UP"); 
  47.                 if(m_flag_swipe_is_on == false) 
  48.                     return true; 
  49.                 m_flag_swipe_is_on = false; 
  50.                 return true; 
  51.             } 
  52.   
  53.             if(event.getAction() == MotionEvent.ACTION_CANCEL) 
  54.             { 
  55.                 Log.i("onTouchEvent", "CANCEL"); 
  56.                 if(m_flag_swipe_is_on == false) 
  57.                     return true; 
  58.                 m_flag_swipe_is_on = false; 
  59.                 return true; 
  60.             } 
  61.   
  62.             Log.i("onTouchEvent", "UNKNOWN"); 
  63.             return true; 
  64.         } 
  65.     }; 
  66.   
  67.     // variable qui enregistre si un balayage est en cours 
  68.     private boolean m_flag_swipe_is_on; 
  69.   
  70.     // variable qui enregistre la dernière position du balayage 
  71.     private float m_x_swipe; 
  72.   
  73.     // variable qui permet de gérer la sensibilité du balayage. Plus cette valeur est petite, plus le balayage est sensible 
  74.     private final int m_move_sensibility_x = 25; 

4.3. L'objet « GuiPositionIndicator »

4.3.1. La déclaration

L'objet « GuiPositionIndicator est déclaré directement dans les ressources du projet :

 
CacherSélectionnez
  1.     <view 
  2.         class="fr.circitor.test.GuiPositionIndicator"  
  3.         android:id="@+id/IdGuiPositionIndicator" 
  4.         android:layout_width="fill_parent" 
  5.         android:layout_height="40sp" 
  6.         /> 

Il n'y a rien de particulier à signaler dans cette déclaration mise à part la ligne « class= » qui fait le lien entre cet objet graphique et la classe « GuiPositionIndicator ».

4.3.2. La partie graphique

La partie « artistique » de l'objet GuiPositionIndicator, se trouve sans surprise dans la fonction « onDraw() » :

 
CacherSélectionnez
  1.     // Fonction appelée par Android pour redessiner l'objet graphique 
  2.     protected void onDraw(Canvas canvas) 
  3.     { 
  4.         super.onDraw(canvas); 
  5.   
  6.         // verifie s'il y a quelque chose 
  7.         if(canvas == null) return; 
  8.         if(WorkSize() == 0) return; 
  9.         //Log.d("GuiPositionIndicator", "onDraw()"); 
  10.   
  11.         // paint color 
  12.         Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 
  13.         paint.setColor(Color.RED); 
  14.         paint.setStyle(Paint.Style.STROKE); 
  15.   
  16.         // get sizes of the object 
  17.         float width = getWidth(); 
  18.         //Log.d("GuiPositionIndicator", "width=" + width); 
  19.         float height = getHeight(); 
  20.         //Log.d("GuiPositionIndicator", "height=" + height); 
  21.   
  22.         // check size to determine if horizontal or vertical view 
  23.         boolean flag_horizontal = true; 
  24.         if(width < height) 
  25.             flag_horizontal = false; 
  26.   
  27.         // compute the radius 
  28.         float radius = width * radiusRatio(); 
  29.         if(flag_horizontal == true) 
  30.             radius = height * radiusRatio(); 
  31.   
  32.         // compute the total size 
  33.         // 2 radius per circle to draw 
  34.         float size = WorkSize() * 2; 
  35.   
  36.         // add space between circles 
  37.         if(WorkSize() > 1) 
  38.             size += (WorkSize() - 1) * nbRadiusBetweenCircle(); 
  39.   
  40.         // multiply by the size of the radius 
  41.         size *= radius; 
  42.   
  43.         // and divide it by 2 
  44.         size /= 2; 
  45.   
  46.         float x = width / 2; 
  47.         float y = height / 2; 
  48.         if(flag_horizontal == true) 
  49.             x -= size; 
  50.         else 
  51.             y -= size; 
  52.   
  53.         // compute offset between circles 
  54.         float offset = (nbRadiusBetweenCircle() + 2) * radius; 
  55.   
  56.         // draw circles 
  57.         for(int boucle = 0; boucle != WorkSize(); boucle++) 
  58.         { 
  59.             boolean fill_it = false; 
  60.             if(WorkPos() == boucle) 
  61.                 fill_it = true; 
  62.             circle(canvas, x, y, radius, fill_it); 
  63.   
  64.             if(flag_horizontal == true) 
  65.                 x += offset; 
  66.             else 
  67.                 y += offset; 
  68.         } 
  69.     } 
  70.   
  71.     private final float radiusRatio() { return (float)0.20; } 
  72.     private final float nbRadiusBetweenCircle() { return (float)0.7; } 
  73.     private final int circleEmptyColor() { return Color.GRAY; } 
  74.     private final int circleFullColor() { return Color.WHITE; } 
  75.   
  76.     private void circle(Canvas canvas, float X, float Y, float Radius, boolean Fill) 
  77.     { 
  78.         // paint color 
  79.         Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); 
  80.         if(Fill == false) 
  81.         { 
  82.             paint.setStyle(Paint.Style.STROKE); 
  83.             paint.setColor(circleEmptyColor()); 
  84.         } 
  85.         else 
  86.         { 
  87.             paint.setStyle(Paint.Style.FILL_AND_STROKE); 
  88.             paint.setColor(circleFullColor()); 
  89.         } 
  90.   
  91.         // draw circle 
  92.         canvas.drawCircle(X, Y, Radius, paint); 
  93.     } 

4.3.3. L'initialisation

L'initialisation de l'objet « GuiPositionIndicator » se fait dans la fonction onCreate() de l'activité :

 
CacherSélectionnez
  1. public class TestActivity extends Activity 
  2. { 
  3.     // l'objet GuiPositionIndicator 
  4.     private GuiPositionIndicator m_ctrl; 
  5.   
  6.     public void onCreate(Bundle savedInstanceState) 
  7.     { 
  8.         ... 
  9.         // récupère l'indicateur de position 
  10.         m_ctrl = (GuiPositionIndicator)findViewById(R.id.IdGuiPositionIndicator); 
  11.   
  12.         // initialise la taille et la position de l'indicateur (pour cet exemple, ces valeurs sont arbitraires) 
  13.         m_ctrl.WorkSize(10); 
  14.         m_ctrl.WorkPos(4); 
  15.         ... 

4.3.4. La modification de la position

La modification de la position de l'objet « GuiPositionIndicator » se fait dans le gestionnaire d'événements de la fenêtre de texte :

 
CacherSélectionnez
  1.                 ... 
  2.                 if(delta_x > 0) 
  3.                     m_ctrl.WorkPosNext(); 
  4.                 else 
  5.                     m_ctrl.WorkPosPrevious(); 
  6.                 ... 

5. La parole est maintenant aux graphistes

Voilà, la « tambouille » Android est maintenant terminée, c'est aux graphistes d'exprimer leur créativité en remplaçant le code de la fonction onDraw(), cette tâche est largement au-dessus de mes possibilités.

À titre d'exemple, voici quelques compteurs qui pourraient être dessinés :

Image non disponible Image non disponible
Image non disponible Image non disponible
Image non disponible Image non disponible

6. Conclusions

Cet article est maintenant terminé et j'espère qu'il vous a montré qu'il est très facile d'implémenter un nouveau type d'objet.

Je suis complètement débutant avec cet environnement de développement et j'avoue que j'ai été agréablement surpris des possibilités graphiques qui sont offertes et des extensions possibles et faciles sous Android malgré un aspect un peu « rustique » au premier abord.

Votre avis et vos suggestions sur ce tutoriel m'intéressent ! Alors après votre lecture, n'hésitez pas : 4 commentaires

Je tiens à remercier ClaudeLELOUP pour ses conseils avisés lors de la relecture de ce tutoriel.