Skip to content

10 février 2011 | Rédigé par Axon

19

Réaliser une custom ProgressBar/SeekBar sur Android [Tutoriel Android n°24]

Dans cet article, on va faire une pierre deux coups, on va apprendre à modifier la SeekBar (comparable au UISlider sur iPhone pour ceux qui connaissent) qui peut s’avérer utile comme timeline sur un lecteur audio/video, et la ProgressBar Horizontale (et uniquement l’horizontale, il existe en effet des ProgressBar dites « indéterminée » que l’ont peut comparer à ce qu’on appelle un loader, ou icône de chargement), qui peut être utilisée pour observer la progression d’un téléchargement par exemple.
Ces deux widget se ressemblent beaucoup et pour cause : ils utilisent le même design (pas du tout à mon goût).

Figure 1 - ProsgressBar/SeekBar d'origines et résultats finaux

Figure 2 - ProgressBar type "loader"

A l’assaut des styles de la ProgressBar (Horizontale !)

Une ProgressBar par défaut se constitue d’une image de type loading, qu’on pourra retrouver dans les fichiers images du SDK sous le nom de « spinner » (exemple : spinner_black_48.png). Pour obtenir une ProgressBar Horizontale (que l’on abrégera par PBH pour la suite de notre article), il suffit d’ajouter un style bien précis à la ProgressBar que l’on a déclaré dans le fichier XML de notre Activity :

<ProgressBar
        android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	style="?android:attr/progressBarStyleHorizontal"
	android:progress="50"
	android:max="100"
	android:secondaryProgress="80"/>

On remarquera ici plusieurs attributs pour notre PBH :

  • Max : permet de définir la valeur totale de la progression de notre barre, je mets toujours 100 pour avoir une notion de pourcentage dans nos calculs, ce qui les simplifiera.
  • Progress : indique tout simplement le niveau de progression
  • secondProgress : indique un deuxième niveau de progression, ce qui peut être utile dans une écoute de musique en streaming par exemple, ou le progress indique la progression de lecture, et le secondProgress indique la progression du téléchargement.
  • Style : le style de notre barre, il fait appelle ici à l’attribut de style (un attribut de style est en fait une référence/un pointeur de style) de la PBH native d’Android.

Retour aux sources…

C’est ici que nous allons intervenir, nous allons étendre le style de la PBH (comme si l’on étendait une classe Java) et en l’appliquant à notre progressBar. La documentation des styles sur Android est très faible, elle ne fait qu’indiquer les styles disponibles, sans vraiment nous indiquer à quoi ils correspondent et comment ils sont construits, nous allons donc faire un tour dans les sources pour nous documenter.
Comme l’indique le fichier themes.xml des sources :

<item name="progressBarStyleHorizontal">@android:style/Widget.ProgressBar.Horizontal</item>

L’attribut de style progressBarStyleHorizontal fait référence au style Widget.ProgressBar.Horizontal, où l’on trouvera les spécifications dans le fichiers styles.xml :

 <style name="Widget.ProgressBar.Horizontal">
   <item name="android:indeterminateOnly">false</item>
   <item name="android:progressDrawable">@android:drawable/progress_horizontal</item>
   <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
   <item name="android:minHeight">20dip</item>
   <item name="android:maxHeight">20dip</item>
 </style>

On ne va pas s’intéresser ici à “inderterminateOnly” et à “indeterminateDrawable », qui concerne les PBH qui n’indique pas l’évolution de progression de la barre (mais vous pouvez également modifier l’image si ça vous chante ;) )

Pour modifier ce style, rien de plus simple !

On va créer un fichier styles.xml dans notre projet, et y insérer notre nouveau style en indiquant qu’il est une extension de Widget.ProgressBar.Horizontal :

<resources>
	<style name="CustomProgressBarHorizontal" parent="android:Widget.ProgressBar.Horizontal">
          <item name="android:progressDrawable">@drawable/custom_progress_bar_horizontal</item>
          <item name="android:minHeight">10dip</item>
          <item name="android:maxHeight">20dip</item>
	</style>
</resources>

Par manque d’imagination, j’ai appelé ce nouveau style CustomProgressBarHorizontal. Vous remarquerez que j’ai modifié la hauteur minimum (minHeight) pour avoir la possibilité d’avoir une barre plus fine, et que j’ai modifié l’image qui est utilisée en tant que progressDrawable (qui en fait un fichier xml, qui définit les images de fond, de progress, et de secondaryProgress de notre barre).

Et là, PAF ! Eclipse (désolé pour ceux qui utilise un autre IDE) n’est pas content et nous indique que le drawable custom_progress_bar_horizontal n’existe pas, ce qui est plutôt logique, puisqu’on ne l’a pas encore créée.

Une fois de plus, on va s’aider des sources pour comprendre comment la classe ProgressBar utilise le fichier xml, il suffit ici d’aller fouiller dans les fichiers de notre SDK : %ANDROID_SDK_PATH%/platforms/android-n/data/res/drawable/ (où n est la version de l’API)

Le fichier progress_horizontal est un fichier XML contenant des drawable, des classes bien pratiques permettant de faire des dessins plus ou moins basiques (dégradé, angle, cercle, etc…) à l’aide du XML, je vous conseille d’aller faire un tour du la documentation des drawable pour vous y familiariser.

Dans ce fichier, on y retrouve un layer-list, qui est en fait un tableau d’autres drawable. Ces drawables sont dessinés les uns au dessus des autres en suivant l’ordre dans lequel ils sont écrits dans le fichier : donc pour notre PBH, il faudra donc dessiner le background, le secondaryProgress et le progress pour finir :

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:id="@android:id/background">
	<shape>
	   <corners android:radius="5dip" />
	   <gradient
		android:startColor="#ff9d9e9d"
		android:centerColor="#ff5a5d5a"
		android:centerY="0.75"
		android:endColor="#ff747674"
		android:angle="270" />
	</shape>
  </item>
  <item android:id="@android:id/secondaryProgress">
	<clip>
	   <shape>
		<corners android:radius="5dip" />
		<gradient
		     android:startColor="#80ffd300"
		     android:centerColor="#80ffb600"
		     android:centerY="0.75"
		     android:endColor="#a0ffcb00"
		     android:angle="270" />
	   </shape>
	</clip>
  </item>
  <item android:id="@android:id/progress">
	<clip>
	   <shape>
		<corners android:radius="5dip" />
		<gradient
		     android:startColor="#ffffd300"
		     android:centerColor="#ffffb600"
		     android:centerY="0.75"
		     android:endColor="#ffffcb00"
		     android:angle="270" />
	   </shape>
	</clip>
  </item>
</layer-list>

Quand on regarde de plus près, on remarque que ces drawables sont de simples rectangles (shapes) comprenant des coins arrondis et un dégradé.

Pour modifier notre PBH, on va ici faire simple et modifier les dégradés (les simplifier et changer les couleurs), cela peut sembler un peu simple, mais cela suffira à modifier notre PBH pour qu’elle puisse corresponde au thème général de votre application et qu’elle soit identique sur tous les terminaux.

Ce qui nous donne les codes suivant, avec un thème bleu à notre PBH :

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:id="@android:id/background">
	<shape>
	   <corners android:radius="5dip" />
	   <gradient
		android:startColor="#ffffffff"
		android:centerColor="#ffdddddd"
		android:centerY="0.50"
		android:endColor="#ffffffff"
		android:angle="270" />
	</shape>
  </item>
  <item android:id="@android:id/secondaryProgress">
	<clip>
	   <shape>
		<corners android:radius="5dip" />
		<gradient
		     android:startColor="#770e75af"
		     android:endColor="#771997e1"
		     android:angle="90" />
	   </shape>
	</clip>
  </item>
  <item android:id="@android:id/progress">
	<clip>
	   <shape>
		<corners android:radius="5dip" />
		<gradient
		     android:startColor="#ff0e75af"
		     android:endColor="#ff1997e1"
		     android:angle="90" />
	   </shape>
	</clip>
  </item>
</layer-list>

On aura donc un fond blanc avec un léger dégradé vers le gris en son centre. On laisse les coins arrondis (border-radius) à 5dip, ce qui nous fera l’effet un demi cercle sur les extrémités de notre PBH si on lui fixe un layout_height à wrap_content (et oui, n’oubliez pas qu’on a mis le minHeight de notre style à 10dip ;) ). En appliquant notre style à la PBH, on aura donc le rendu suivant :

Figure 3 - Custom ProgressBar Horizontale

Remarque : cela ne vous coûte rien de garder les mêmes ID pour vos drawables (android.R.id.progress par exemple), ils sont utilisées dans la classe ProgressBar.java des sources, cela vous évitera quelques petits soucis.

Customisation de la SeekBar


Quand on regarde le visuel d’une SeekBar, ce n’est rien d’autre qu’une progressBar Horizontale avec un petit « sélectionneur « en plus : chez Google, ils l’ont baptisé thumb (traduction anglaise de « pouce »).

Libre à chacun d’apprécier ou pas le design de ce thumb, personnellement, ce n’est toujours pas mon cas.

Je ne vais bien entendu pas m’étaler ici sur la modification de la barre de progression, il suffit de reprendre la première partie de cet article, mais je vous ferai juste remarquer que si l’on regarde une fois de plus les sources, le progressDrawable de la seekBar fait appel au même fichier que la PBH :

<style name="Widget.SeekBar">
        <item name="android:indeterminateOnly">false</item>
        <item name="android:progressDrawable">@android:drawable/progress_horizontal</item>
        <item name="android:indeterminateDrawable">@android:drawable/progress_horizontal</item>
        <item name="android:minHeight">20dip</item>
        <item name="android:maxHeight">20dip</item>
        <item name="android:thumb">@android:drawable/seek_thumb</item>
        <item name="android:thumbOffset">8dip</item>
        <item name="android:focusable">true</item>
    </style>

On va s’intéresser ici aux items « thumb » et « thumbOffset » (décalage du thumb).
La customisation du thumb va se faire de la même manière que celle de la PBH, on va étendre le style du widget SeekBar, en modifiant le drawable du thumb.

Remarque : il est également possible de changer le thumb et le thumbOffSet directement dans les attributs d’une SeekBar lors sa déclaration dans le layout de notre Activity, mais je trouve plus propre le fait de le faire dans l’extension du style, de plus, si vous avez plusieurs SeekBar, vous n’aurez pas à ses attributs à chaque fois, c’est le style que vous aurez appliqué qui s’en chargera.

Dans notre nouveau style de SeekBar, on change donc notre thumb :

<item name="android:thumb">@drawable/seek_bar_thumb</item>

Et, comme dans les sources, ce fichier sera un fichier XML comprenant un <selector> , un autre drawable (stateListDrawable) qui définit plusieurs autres drawables selon l’état du widget, des états qui dépendent la plupart du temps des actions de l’utilisateur : focus, pressed, selected, etc… :

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"
          android:drawable="@drawable/custom_thumb_state_pressed" />
    <item android:state_focused="true"
          android:drawable="@drawable/custom_thumb_state_selected" />
    <item android:state_selected="true"
          android:drawable="@drawable/custom_thumb_state_selected" />
    <item android:drawable="@drawable/custom_thumb_state_default" />
 
</selector>

Ces 4 items, dont les noms parlent d’eux même, sont dans notre cas d’autres fichiers XML, comprenant des formes (shape), comme on a pu les rencontrer dans la première partie de cette article (cf. documentation des drawable ).

Voici par exemple celui qui décrit l’état « normal » (par défaut) de notre thumb (les deux autres  sont quasiment identiques, juste les couleurs définies sont modifiées) :

<shape
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:shape="oval">
	<size
		android:width="35dip"
		android:height="35dip" />
	<stroke
		android:width="1dip"
		android:color="#ffffffff" />
	<gradient
		android:startColor="#ffcdcdcd"
		android:endColor="#fff8f8f8"
		android:angle="270"
		android:type="linear" />
</shape>

Un simple disque, de rayon 35dip, avec une bordure et un dégradé :

Figure 5 - SeekBar avec thumb par défaut

Figure 4 - seekBar etat focused

Figure 7 - seekBar état pressed

Figure 6 - seekbar état normal

Lorsque l’on glisse le sélecteur au fond à droite, on remarque que l’image est coupée, chose qui se reproduira du côté gauche, et c’est là qu’intervient le thumbOffSet.

Quel ThumbOffSet choisir ?

Le style de la SeekBar android, qu’on a pu voir plus haut dans cet article, définit un thumbOffSet par défaut à 8dip, pourquoi 8dip ? Il n’y a qu’à regarder les fichiers images utilisées pour le thumb de base !

Pour cela, sur votre poste, regardez de plus près

%ANDROID_SDK_PATH%/platforms/android-7/data/res/drawable-mdpi/seek_thumb_normal.jpg

(pour un terminal avec une densité moyenne, un dip est égal à un pixel, c’est pour cela que j’ai choisis le dossier drawable-mdpi : plus de renseignements sur les densité d’écran ici).

On s’aperçoit que l’image à une largeur de 32 pixel, et qu’elle possède deux zones de vide de 8 pixels de chaque côté du thumb qui y est dessiné, ce qui explique le 8 pour le thumbOffset dans le style par défaut des seekBar.

Remarque : rien ne vous empêche effectivement de créer vos thumb avec des fichiers image, à l’aide notamment de Photoshop ou Gimp, j’ai choisis ici de les créer à l’aide des drawable android et de leur xml.

Comme nos thumb sont des drawable en xml, ils n’auront pas de marges vides, nous allons donc mettre notre thumbOffset à 0dip dans notre style :

<style name="CustomSeekBar" parent="android:Widget.SeekBar">
   <item name="android:progressDrawable">@drawable/custom_seek_bar</item>
   <item name="android:thumb">@drawable/seek_bar_thumb</item>
   <item name="android:thumbOffset">0dip</item>
</style>

Et on l’insère dans le layout de notre Activity :

<SeekBar
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:progress="50"
	android:max="100"
	style="@style/CustomSeekBar"
	android:secondaryProgress="80"
	android:layout_margin="5dip" />

Il n’y a plus qu’à admirer le résultat final de notre article  ! :)

Vous pouvez trouver les sources du projet ici.

Je tenais à remercier Alex (dit Sakaroz) qui a écrit ce tutorial Android très intéressant. Je vous invite à aller faire un tour sur son blog : http://blog.sakaroz.com/ et son site pro : http://www.sakaroz.com/

Découvrez d'autre articles de la catégorie Tutoriels Android

Encore un peu de lecture :

19 Commentaires Poster un commentaire
  1. Cédric
    10 fév 2011

    Dans mon cas, la seule fois où j’ai essayé d’utiliser une progressBar c’était dans une customDialog, et dès que j’essayais de la mettre à jour l’application plantait…

    Je trouve dommage de ne pas trouver de code java dans ce tutorial… :/

  2. 10 fév 2011

    Quelle erreur avais tu pour ton plantage ? :)

    Tu ne devrais pas trouver dommage de ne pas avoir de java pour la customisation de widget : je m’explique

    je pense que c’est un un point positif d’Android de ne pas toucher à la logique applicative lorsque l’on a envie de modifier l’UI, c’est le cas ici et c’est en partie ce que j’ai voulais montrer, on a complètement modifier les progressBar sans toucher une ligne de code java, et je trouve ça plutôt pratique et clair pour les développeurs.

    Ensuite, pour ce qui est de l’utilisation de ces widget, ce n’était pas le sujet de cet article :)

  3. Cédric
    11 fév 2011

    Je ne sais plus l’erreur que j’avais, il faudrait que je m’y remette… ça fait déjà quelques mois ^^ »

  4. Alaa
    7 mar 2011

    Savez-vous comment faire un text separator entre plusieurs checkbox:
    2 options checkbox et un text separator(comme titre pour les 2 prochaines options).
    Merci à vous.

  5. jonathan
    11 mar 2011

    Pour un tutoriel en réalité augmenté, je propose un truc du genre d’une application que j’ai faite (Boussole et Carte) qui est sur l’android market. Il y a une version lite (Boussole et Carte lite) qui permet d’utiliser les fonctions de réalité augmentée. Cette fonctionnalité est détaillée dans l’aide de l’application. Si ça t’intéresse toi et d’autres tutomobile addicts, je t’envoie tout ça.

  6. reda
    13 mai 2011

    Excellent !

  7. Thundersword
    3 juil 2011

    Bonjour,
    Tout d’abord, bravo pour cet excellent tutoriel, très bien expliqué, tout niquel !
    Maintenant, j’aimerais réaliser une seekbar ainsi qu’un progressbar, verticale cette fois ci ! Seulement étant débutant, je n’arrive pas encore à très bien me débrouiller… Pourriez vous m’aider ?
    Merci d’avance.

  8. 18 juil 2011

    La progressBar verticale n’est pas fournie par Android par défaut, il te faut créer une progressBar verticale toi même :)

  9. Hary
    19 juil 2011

    Bonjour, merci pour le tuto
    je voudrais savoir comment je pourrais lier cette barre de progression à un processus téléchargement que je voudrais effectuer.
    En fait dans les tutos que j’ai lu, progressbar se crée en JAVA notamment
    mProgressDialog = new ProgressDialog(this);
    mProgressDialog.setMessage(« Downloading file.. »); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    mProgressDialog.setCancelable(false);
    mProgressDialog.show();
    return mProgressDialog;

    alors que dans notre cas ça se crée en XML

    Merci

  10. DeLeTeD
    5 août 2011

    Bonjour,

    merci pour tout ces tutos

    je voulais savoir comment il etait possible de refresh une progressBar qui se trouve dans une notification (sans recréer un notification qui fera lager le téléphone)

    merci

  11. gohu
    26 oct 2011

    Salut,
    je suis a la recherche d’une seekbar spécifique. Je sais pas si elle existe, mais j’aimerai faire comme l’iphone avec les points qui permet de savoir à quel page on est.

    Pensez vous cela possible?

  12. 2 avr 2012

    Plop !

    Just un petit commentaire pour dire merci, car ce tuto m’a fort dépanné. Effectivement, la doc officielle sur le sujet est juste lamentable. Ce post m’a donc évité quelques arrachages de cheveux (déjà qu’il ne m’en reste plus beaucoup…)

  13. Jafar
    10 avr 2012

    C’est exactement ce que je cherchais mais je ne comprends pas comment il faut l’utiliser.
    J’ai juste besoin de la progress bar.
    J’ai créé un fichier styles.xml dans mon projet et j’y ai ajouté le code XML ressources comme indiqué mais ensuite? que faire du code XML layer-list avec les shapes ?

  14. Jafar
    10 avr 2012

    ah et by the way, le styles.xml il faut le mettre où? dans drawable?

  15. Jafar
    10 avr 2012

    Bon ben j’ai trouvé: styles.xml va dans res/values/ et les shapes dans un fichier nommé custom_progress_bar_horizontal.xml et placé dans drawable.

  16. Nicolas
    2 mai 2013

    Vraiment très bon tuto, simple et claire. Il m’as beaucoup aider, merci !

  17. saif
    11 mai 2013

    slt
    j’ai une application android avec une carte arduino ADK et je veut afficher le signal cardiographie(ECG) sur un téléphone android .
    j’ai une problème qu niveaux de la laison entre la carte arduino et le téléphone.

Trackbacks & Pingbacks

  1. Les tweets qui mentionnent Réaliser une ProgressBar/SeekBar personnalisée sur Android [Tutoriel Android n°24] | Tuto Mobile -- Topsy.com
  2. Sakaroz » Réaliser un custom ProgressBar / SeekBar sur Android

Une question, une suggestion, une opinion? Partagez ce que vous pensez, laissez un commentaire.

(obligatoire)
(obligatoire)

Note: Votre adresse email ne sera jamais publiée.

Suivez les réponses aux commentaires