French Comment font-ils ? #3

Informatix

Expert
Licensed User
Comment font-ils ? #3​

Ce troisième tutoriel revient sur l'interface de PlayerPro de BlastOn LLC. Nous allons reproduire la liste des artistes et le fonctionnement des vues personnalisées de l'égaliseur. Nous allons aussi ajouter la fonction de glisser/déposer (drag & drop) à la barre d'onglets et déporter l'affichage du ScrollPanel.

Cet article suppose que vous connaissez déjà les bases du développement avec B4A et que vous avez lu les deux premiers tutoriels.

Deuxième partie: la liste des artistes

attachment.php


Nous avons vu dans le tutoriel précédent comment reproduire la mise en page générale de l'application. Je ne vais donc m'intéresser cette fois-ci qu'à la création de la liste des artistes. Elle présente une particularité: en cliquant sur un artiste, on peut faire apparaître la liste des albums en dessous. Dans l'API d'Android, cette vue est une ExpandableListView que nous pouvons reproduire sans problème avec la classe CheckList.

Dans le premier tutoriel, je vous avais montré comment fabriquer un masque d'item dans le designer et comment le charger dans l'application. Je ne vais pas utiliser cette méthode ici car j'ai besoin d'un maximum de rapidité. Il faut savoir que le chargement d'un fichier .bal avec LoadLayout est très lent. J'ai chronométré dans plusieurs projets les deux méthodes (chargement de vues créées dans le designer vs. création dans le code) et la différence varie de 5 à 7 suivant le projet. Dans le cas présent, mon appareil (un Huawei Honor monocore 1,4 Ghz) met 35 secondes pour construire 1000 entrées dans la liste des artistes avec la première méthode (chargement de vues créées dans le designer) et seulement 5 avec la deuxième (création dans le code). Je conseille donc d'éviter d'utiliser LoadLayout dans une boucle. Cela dit, vous pouvez quand même utiliser le designer pour faire du prototypage (la maquette visuelle vous aide à choisir les bonnes valeurs des propriétés des vues, notamment les propriétés de positionnement et de dimension).

Maquette pour l'item de la liste des artistes:
attachment.php


Avant de créer mes vues, je dois modifier mon gestionnaire d'évènements (voir le tutoriel précédent) pour appeler ma nouvelle fonction CreateArtistList:
B4X:
Sub Artists_Click(ActionBar As ClsActionBar, Btn As View)
   If LastBtn <> Btn Then
      TabChange(Btn, "tab_artists_defaut.png", "tab_artists_select.png")
      CreateArtistList
   End If
End Sub

Dans CreateArtistList, je commence par charger les images statiques en mémoire, c'est-à-dire les images qui ne sont pas propres à un item (l'image de fond, l'icône indiquant qu'une extension est possible et l'icône après extension). Cela m'évitera de lire des fichiers à chaque création d'item. Cela m'assure aussi qu'il n'y a qu'une seule copie en mémoire de ces images:
B4X:
bdDefaultBackground.Initialize(LoadBitmap(File.DirAssets, "bg_liste.png"))
bdCanExpandArrow.Initialize(LoadBitmap(File.DirAssets, "can_expand.png"))
bdExpandedArrow.Initialize(LoadBitmap(File.DirAssets, "expanded.png"))
Les trois variables ont été déclarées dans Globals avec le type BitmapDrawable.

Je crée ensuite le ScrollView qui héberge la liste et permet de la faire défiler:
B4X:
svArtist.Initialize2(0, "svArtist")
pnlContent.AddView(svArtist, 0, 0, pnlContent.Width, pnlContent.Height)
Puis la liste elle-même:
B4X:
lstArtist.Initialize(Me, svArtist, "", "lstArtist_Click", "", 1dip)
Le ScrollView et la liste ont été déclarés dans Globals:
B4X:
Dim svArtist As ScrollView
Dim lstArtist As ClsCheckList

Pour reproduire l'apparence personnalisée de la liste de PlayerPro, je dois gérer moi-même l'apparence de la liste et ne pas laisser cette gestion à la classe (par défaut, celle-ci ne gère pas les indicateurs d'extension). Je vais donc lui indiquer que je prends la main en définissant le gestionnaire des évènements OnPaint:
B4X:
lstArtist.SetOnPaintListener("PaintArtist")
Puis je crée le gestionnaire:
B4X:
Sub PaintArtist(pnlItem As Panel, State As Int)
   Select State
      Case 1   ' Extended
         pnlItem.Color = Colors.RGB(120, 190, 30) 'Green
         Dim ivExpandState As ImageView
         ivExpandState = pnlItem.GetView(3)
         ivExpandState.Background = bdExpandedArrow
      Case 4   ' Pressed
         pnlItem.Color = Colors.RGB(120, 190, 30) 'Green
      Case Else
         pnlItem.Background = bdDefaultBackground
         Dim ivExpandState As ImageView
         ivExpandState = pnlItem.GetView(3)
         ivExpandState.Background = bdCanExpandArrow
   End Select
End Sub

Désormais, à chaque fois qu'un item de la liste va changer d'état, je vais en être informé et je vais pouvoir modifier son apparence en conséquence. Je dois gérer au moins trois états:
0 (default): l'item n'est ni étendu, ni touché
attachment.php
4 (pressed): l'utilisateur vient d'appuyer sur l'item
attachment.php
1 (extended): l'item a été étendu (nous verrons plus loin comment remplir l'extension)
attachment.php

Je n'oublie pas dans CreateArtistList de définir la couleur du séparateur entre les items:
B4X:
lstArtist.DividerColor = Colors.Black

Pour remplir ma liste, j'ai besoin d'une fonction de création d'item:
B4X:
Sub CreateArtistItem(Artist As String, PicFile As String, NbOfAlbums As Int) As Panel
   Dim pnl As Panel
   pnl.Initialize("")
   pnl.Background = bdDefaultBackground

   Dim ivPhoto As ImageView
   ivPhoto.Initialize("")
   ivPhoto.Gravity = Gravity.FILL
   ivPhoto.Bitmap = LoadBitmap(strPicDir, PicFile)
   pnl.AddView(ivPhoto, 0, 0, 65dip, 65dip)

   Dim lblArtistName As Label
   lblArtistName.Initialize("")
   lblArtistName.TextColor = Colors.White
   lblArtistName.TextSize = 18
   lblArtistName.Typeface = Typeface.DEFAULT_BOLD
   lblArtistName.Gravity = Gravity.TOP
   lblArtistName.Text = Artist
   Ellipsize(lblArtistName)
   AddShadow(lblArtistName, Colors.Black)
   pnl.AddView(lblArtistName, 75dip, 10dip, 100%x - 110dip, 25dip)

   Dim lblNbOfAlbums As Label
   lblNbOfAlbums.Initialize("")
   lblNbOfAlbums.TextColor = Colors.RGB(220, 220, 220)
   lblNbOfAlbums.TextSize = 14
   lblNbOfAlbums.Gravity = Gravity.BOTTOM
   If NbOfAlbums > 1 Then
      lblNbOfAlbums.Text = NbOfAlbums & " albums"
   Else
      lblNbOfAlbums.Text = "1 album"
   End If
   AddShadow(lblNbOfAlbums, Colors.Black)
   pnl.AddView(lblNbOfAlbums, 75dip, 34dip, 100%x - 110dip, 20dip)

   Dim ivExpandState As ImageView
   ivExpandState.Initialize("")
   ivExpandState.Gravity = Gravity.FILL
   ivExpandState.Background = bdCanExpandArrow
   pnl.AddView(ivExpandState, 100%x - 36dip, 10dip, 32dip, 32dip)

   Return pnl
End Sub
C'est dans cette fonction que j'évite de recourir à LoadLayout. Toutes les vues sont créées et paramétrées dans le code.

Ma fonction fait appel à une autre fonction, Ellipsize, qui ajoute des points de suspension si le nom de l'artiste est trop long:
B4X:
Sub Ellipsize(lbl As Label)
   Dim r As Reflector
   r.Target = lbl
   r.RunMethod2("setLines", 1, "java.lang.int")
   r.RunMethod2("setHorizontallyScrolling", True, "java.lang.boolean") 
   r.RunMethod2("setEllipsize", "END", "android.text.TextUtils$TruncateAt")
End Sub

Je remplis ma liste en faisant appel à CreateArtistItem. Exemple:
B4X:
Dim pnlArtist As Panel
pnlArtist = CreateArtistItem("Big Country", "big_country_square.jpg", 1)
lstArtist.AddCustomItem("BigCountry1", pnlArtist, 65dip)
Je termine mon remplissage avec:
B4X:
lstArtist.ResizePanel

Reste le cas des extensions. J'ai indiqué lors de l'initialisation de ma liste que je voulais gérer l'évènement OnClick. J'écris le gestionnaire:
B4X:
Sub lstArtist_Click(pnlItem As Panel, ID As Object)
   If lstArtist.HasExtraContent AND lstArtist.ExtendedItemID = ID Then
      lstArtist.CollapseItem
   Else
      Dim pnlAlbums As Panel
      pnlAlbums.Initialize("")
      Dim NbOfAlbums As Int
      NbOfAlbums = AddAlbumItems(pnlAlbums, ID)
      lstArtist.ExtendItem(ID, pnlAlbums, 65dip * NbOfAlbums)
   End If
End Sub
Ce gestionnaire crée une extension quand l'item cliqué n'en a pas et la contracte si l'item en a déjà une.
L'extension est remplie avec la fonction AddAlbumItems qui ne sera pas détaillée ici. Cette fonction fait appel à une fonction CreateAlbumItem qui est très semblable à CreateArtistItem.

Résultat:
attachment.php


Troisième partie: les petits plus de l'interface

L'interface de PlayerPro a deux caractéristiques que nous allons reproduire: la possibilité de changer l'ordre des boutons de la barre d'onglets en faisant un clic long et l'affichage de la première lettre du nom de l'artiste quand on utilise la poignée de défilement rapide.

La classe ActionBar que nous avons utilisée pour les onglets propose une fonctionnalité de glisser/déposer (drag & drop). Il suffit de la mettre en oeuvre.
Je commence par modifier l'initialisation de mes boutons pour indiquer la prise en charge du clic long (il est inutile dans le cas présent d'avoir des gestionnaires personnalisés; tous les boutons peuvent avoir le même). Exemple:
B4X:
aBtn(0) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_artists_defaut.png"), "", 2, 1, "Artists_Click", "Btn_LongClick")
aBtn(1) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_albums_defaut.png"), "", 2, 2, "Albums_Click", "Btn_LongClick")

J'écris le gestionnaire du clic long:
B4X:
Sub Btn_LongClick(ActionBar As ClsActionBar, Btn As View)
   If LastBtn <> Btn Then
      'Replaces the transparent background by a solid one
      Btn.Background = btnBackground
   End If
   abTabs.StartDragAndDrop(Btn, hsvTabBar, "Btn_AfterDrop")
End Sub
Et la fonction appelée à la fin du glisser/déposer:
B4X:
Sub Btn_AfterDrop(ActionBar As ClsActionBar, Btn As View)
   If LastBtn = Btn Then
      'Selected btn: redraws the nine-patch erased after the drag&drop
      Btn.Background = LoadNinePatchDrawable("bg_select")
   End If
End Sub
La classe redessine le fond du bouton à la fin de l'opération. Je dois donc recharger le 9-patch clair (cf. le deuxième tutoriel) si le bouton est l'onglet sélectionné.

Exemple de glisser/déposer:
attachment.php


Passons maintenant à la poignée de défilement rapide. On veut qu'elle affiche la première lettre de l'artiste dans un Label séparé. C'est assez simple avec la classe ScrollPanel. Je déclare cette classe et le Label dans Globals:
B4X:
Dim spFastScrollArtist As ClsScrollPanel
Dim lblFirstLetter As Label
J'initialise ma classe dans CreateArtistList:
B4X:
spFastScrollArtist.Initialize(svArtist, 60dip, 52dip, False) 'No cache
spFastScrollArtist.ReplaceBackground(spFastScrollArtist.LoadDrawable("scrollbar_handle_accelerated_anim2"))
J'initialise le Label et je le place au centre de l'écran:
B4X:
lblFirstLetter.Initialize("")
lblFirstLetter.Background = spFastScrollArtist.LoadDrawable("toast_frame")
lblFirstLetter.Gravity = Gravity.CENTER_HORIZONTAL + Gravity.CENTER_VERTICAL
lblFirstLetter.TextSize = 20
lblFirstLetter.TextColor = Colors.White
lblFirstLetter.Typeface = Typeface.DEFAULT_BOLD
pnlContent.AddView(lblFirstLetter, (lstArtist.getWidth - 70dip) / 2, (lstArtist.getHeight - 70dip) / 2, 70dip, 70dip)
Je demande ensuite l'affichage de la première lettre de chaque item:
B4X:
Sub svArtist_ScrollChanged(Position As Int)
   spFastScrollArtist.DisplayFirstChar(Position)
End Sub

Problème: la première lettre s'affiche sur le ScrollPanel au lieu du Label et le Label reste affiché en permanence. Pour remédier à tout ça, j'ajoute dans CreateArtistList:
B4X:
spFastScrollArtist.GetLabel.Visible = False
lblFirstLetter.Visible = False 'Initially, the Label is hidden
spFastScrollArtist.SetOnShowHideEvent(Me, "spArtist_ShowHide")
spFastScrollArtist.SetOnTextUpdatedEvent(Me, "spArtist_TextUpdated")
Puis je crée les deux gestionnaires d'évènements chargés de modifier la visibilité et le contenu du Label:
B4X:
Sub spArtist_ShowHide(SenderSP As Object, Visible As Boolean)
   If Visible = False Then
      lblFirstLetter.Visible = False
   Else
      lblFirstLetter.Visible = spFastScrollArtist.ScrollPanelMovedByUser
   End If
End Sub
Sub spArtist_TextUpdated(SenderSP As Object, Text As String)
   lblFirstLetter.Text = Text
End Sub

Et voilà:
attachment.php


À noter: j'ai initialisé le ScrollPanel sans activer son cache. Ce n'est pas gênant ici car la liste est petite, mais si votre liste fait plus d'une centaine d'élements, je vous conseille vivement de l'activer. Inconvénient: il faudra appeler RefreshCache à chaque modification du contenu ou de l'ordre de la liste (ce qui, dans le cas présent, ne devrait pas arriver).

4e et dernière partie dans le message suivant...
 

Attachments

  • PlayerPro_0.jpg
    PlayerPro_0.jpg
    51.8 KB · Views: 4,084
  • PlayerPro_1.png
    PlayerPro_1.png
    10.4 KB · Views: 4,077
  • PlayerPro_3.jpg
    PlayerPro_3.jpg
    34.9 KB · Views: 4,077
  • PlayerPro_4.png
    PlayerPro_4.png
    21.1 KB · Views: 4,042
  • PlayerPro_5.jpg
    PlayerPro_5.jpg
    23.3 KB · Views: 4,084
  • PlayerPro_20.png
    PlayerPro_20.png
    70.5 KB · Views: 4,115
  • PlayerPro_21.png
    PlayerPro_21.png
    57.4 KB · Views: 4,095
  • PlayerPro_24.png
    PlayerPro_24.png
    71.7 KB · Views: 4,108

Informatix

Expert
Licensed User
Quatrième partie: les vues personnalisées de l'égaliseur

Je vais m'intéresser maintenant à un autre écran de PlayerPro: celui de l'égaliseur.

attachment.php


Il y a ici des vues qui n'existent pas dans les vues standards d'Android: des SeekBars verticales, des boutons rotatifs et un bouton On/Off qui ne ressemble pas au ToggleButton habituel. Le reste (Label, Spinner, Button, SeekBar, ImageView) est très classique. Je vais reproduire uniquement les vues personnalisées car la mise en page de l'ensemble ne présente pas d'intérêt particulier.

Voyons tout d'abord le ToggleButton. PlayerPro le décline en plusieurs versions:
attachment.php


En l'utilisant, on se rend compte qu'il s'agit d'un ensemble de quatre images (deux pour les états statiques: on/off, et deux pour les états dynamiques: on vers off/off vers on). À première vue, il semble possible de reproduire ce fonctionnement avec les événements OnDown, OnUp et OnClick d'un Button, mais cette solution ne fonctionne pas très bien car si OnUp permet bien de gérer le cas où le clic ne se fait pas (le doigt a quitté le bouton), il entre en conflit avec OnClick dans le cas contraire. Nous allons donc recourir à une autre solution, très simple, utilisant deux StateListDrawables. Je déclare un Button et quatre images dans Globals:
B4X:
Dim btnEQOnOff As Button
Dim bdEqOn As BitmapDrawable
Dim bdEqOnToOff As BitmapDrawable
Dim bdEqOff As BitmapDrawable
Dim bdEqOffToOn As BitmapDrawable
Dans le code, je charge mes images et j'initialise mon bouton à l'état OFF:
B4X:
bdEqOn.Initialize(LoadBitmap(File.DirAssets, "eqon.png"))
bdEqOff.Initialize(LoadBitmap(File.DirAssets, "eqoff.png"))
bdEqOnToOff.Initialize(LoadBitmap(File.DirAssets, "eqontooff.png"))
bdEqOffToOn.Initialize(LoadBitmap(File.DirAssets, "eqofftoon.png"))

btnEQOnOff.Initialize("btnEQOnOff")
ChangeBtnState(False)
Activity.AddView(btnEQOnOff, 80dip, 5dip, 100dip, 50dip)

Je viens d'appeler la fonction ChangeBtnState. C'est elle qui va permuter les deux StateListDrawables (l'une pour l'état ON, l'autre pour l'état OFF). La propriété Tag du bouton mémorise l'état en cours:
B4X:
Sub ChangeBtnState(IsOn As Boolean)
   Dim sd As StateListDrawable
   sd.Initialize
   If IsOn Then
      sd.AddState(sd.State_Pressed, bdEqOnToOff)
      sd.AddCatchAllState(bdEqOn)
      btnEQOnOff.Tag = "ON"
   Else
      sd.AddState(sd.State_Pressed, bdEqOffToOn)
      sd.AddCatchAllState(bdEqOff)
      btnEQOnOff.Tag = "OFF"
   End If
   btnEQOnOff.Background = sd
End Sub

Je n'ai plus qu'à déclencher le changement de StateListDrawable après un clic:
B4X:
Sub btnEQOnOff_Click
   ChangeBtnState(btnEQOnOff.Tag = "OFF")
End Sub

Voici une variante un peu plus compliquée, mais utile si vous voulez ajouter des animations ou ne pas annuler la pression si le doigt sort du bouton. Le Button est remplacé par un Panel:
B4X:
pnlEQOnOff.Initialize("pnlEQOnOff")
pnlEQOnOff.Background = bdEqOff
pnlEQOnOff.Tag = "OFF"
Activity.AddView(pnlEQOnOff, 80dip, 5dip, 100dip, 50dip)

Tout est fait dans le gestionnaire de l'événement OnTouch:
B4X:
Sub pnlEQOnOff_Touch (Action As Int, X As Float, Y As Float)
    If Action = 0 Then ' DOWN
        If pnlEQOnOff.Tag = "OFF" Then
            pnlEQOnOff.Background = bdEqOffToOn
            pnlEQOnOff.Tag = "OFF to ON"
        Else
            pnlEQOnOff.Background = bdEqOnToOff
            pnlEQOnOff.Tag = "ON to OFF"
        End If
    Else If Action = 2 Then ' MOVE
        If X < 0 OR X > pnlEQOnOff.Width OR Y < 0 OR Y > pnlEQOnOff.Height Then
            ' Out of bounds: the user action is cancelled
            If pnlEQOnOff.Tag = "OFF to ON" Then
                pnlEQOnOff.Background = bdEqOff
                pnlEQOnOff.Tag = "OFF"
            Else If pnlEQOnOff.Tag = "ON to OFF" Then
                pnlEQOnOff.Background = bdEqOn
                pnlEQOnOff.Tag = "ON"
            End If
        End If
    Else If Action = 1 Then ' UP
        If pnlEQOnOff.Tag = "OFF to ON" Then
            pnlEQOnOff.Background = bdEqOn
            pnlEQOnOff.Tag = "ON"
        Else If pnlEQOnOff.Tag = "ON to OFF" Then
            pnlEQOnOff.Background = bdEqOff
            pnlEQOnOff.Tag = "OFF"
        End If
    End If
End Sub

Passons à la SeekBar verticale. PlayerPro la décline en plusieurs versions:
attachment.php


Pour reproduire cette SeekBar, il faut la classe InputSlider et deux images: la réglette en fond et le curseur mobile par dessus. Je déclare la classe dans Globals:
B4X:
Dim isEQFrequency As InputSlider
Dans le code, j'initialise la classe et je charge les images:
B4X:
isEQFrequency.Initialize(Me, "isEQFrequency", Activity, 20dip, 90dip, 40dip, 200dip)
isEQFrequency.SetBackGroundImage(LoadBitmap(File.DirAssets, "bg_eq.png"))
isEQFrequency.SetPointerImage(LoadBitmap(File.DirAssets, "eqthumb.png"))
Je dimensionne mon curseur:
B4X:
isEQFrequency.SetPtrSize(0, 0, 40dip, 40dip)
Voilà c'est fini.

Vous pouvez aussi réaliser cette SeekBar avec la classe VSeekbar.

Terminons ce tour des vues personnalisées par le bouton rotatif:
attachment.php


Il s'agit vraisemblablement d'un Panel avec trois images dedans (le bouton rotatif, le cadran et le nom du bouton). Pour dessiner le nom incurvé, on pourrait utiliser la bibliothèque Reflection pour accéder à la fonction drawTextOnPath de l'API Android, mais il est nettement plus simple de concevoir une image dans un logiciel de dessin et de l'importer dans l'application. L'inconvénient, c'est que ça ne facilite pas la traduction (pour chaque langue, il faut créer une image et l'inclure dans l'application).

J'ai décidé cette fois-ci de vous fournir un code source à télécharger. Bien sûr, vous ne trouverez pas dedans les images ou le code de PlayerPro, mais vous verrez que mes algorithmes (simples et complets) permettent le même fonctionnement. J'ai inclus deux autres types de bouton: les sélecteurs rotatifs et les boutons sans fin. Ce code a besoin de la bibliothèque BitmapPlus pour fonctionner (la bibliothèque qui accompagne la classe CustomGallery).

Dans BitmapPlus, j'utilise la fonction matricielle de rotation de l'API d'Android pour dessiner le bouton avec son nouvel angle. J'aurais pu le faire avec la fonction DrawBitmapRotated du Canvas, mais cette fonction n'a pas de filtre et le résultat n'est pas très beau, même s'il est plus net. Voici une comparaison (BitmapPlus en haut, Canvas.DrawBitmapRotated en bas):
attachment.php


Bonne lecture.

<<< Comment font-ils ? #2

L'auteur: Frédéric Leneuf-Magaud. Je suis développeur professionnel depuis le début des années 90 et j'ai conçu ou participé à une centaine d'applications. Je travaille actuellement pour l'administration française dans une équipe de supervision de serveurs. Le développement sous Android fait partie de mes loisirs.
 

Attachments

  • PlayerPro__0_0.jpg
    PlayerPro__0_0.jpg
    32 KB · Views: 4,059
  • PlayerPro__1.png
    PlayerPro__1.png
    23.7 KB · Views: 4,055
  • PlayerPro__2.png
    PlayerPro__2.png
    16.1 KB · Views: 4,031
  • PlayerPro__3.png
    PlayerPro__3.png
    30.8 KB · Views: 4,034
  • PlayerPro__4.png
    PlayerPro__4.png
    64.1 KB · Views: 4,072
  • RotaryClub_fixed.zip
    89.3 KB · Views: 383
Last edited:

PicTech

Member
Licensed User
Salut Frédéric,

EXTRÊMEMENT intéressant ton tutoriel sur les interfaces graphiques, j'ai hâte d'être rendu là mais je vais avoir pas mal de choses à étudier avant. Le bouton en aluminium brossé est réellement beau.

Je viens de tester ton exemple "RotaryClub" et je trouve le mécanisme vraiment au poil. Par contre je remarque une erreur de réalisme, quand le bouton tourne, le reflet tourne en même temps comme si le soleil ou la lampe du salon tournait autour du bouton. Pythagore serait content! :)

Ce qui amène ma question: Peut-on faire une couche fixe opaque "bouton sans pointeur" et une couche semi-transparente avec seulement un pointeur qui tourne par dessus? Ayoye! Tout un contrat mais ça donnerait un effet d'une perfection absolue.

Prochaine,
Richard.
 

Informatix

Expert
Licensed User
Ce qui amène ma question: Peut-on faire une couche fixe opaque "bouton sans pointeur" et une couche semi-transparente avec seulement un pointeur qui tourne par dessus? Ayoye! Tout un contrat mais ça donnerait un effet d'une perfection absolue.

Tout à fait. Dans RotaryClub, j'affiche un cadran autour du bouton (bg_knob). Si, dans ton logiciel de dessin, tu ajoutes à ce cadran le dessin d'un bouton, tu auras ton fond fixe. Tu n'as plus qu'à remplacer rotatknob.png par un simple dessin de pointeur sur fond transparent.
C'est vrai que c'est une bonne idée, mais curieusement, je n'ai jamais vu ça dans une appli avec bouton rotatif.
 

PicTech

Member
Licensed User
Merci d'approuver l'idée! :) Je vois que tu as coupé court avec le problème du bouton sans fin...

Peux-tu m'expliquer pourquoi il a disparu quand j'ai seulement modifié les images ?

Prochaine,
Richard.
 

Informatix

Expert
Licensed User
Merci d'approuver l'idée! :) Je vois que tu as coupé court avec le problème du bouton sans fin...

Peux-tu m'expliquer pourquoi il a disparu quand j'ai seulement modifié les images ?

Prochaine,
Richard.

Parce que je rendais visible ou non le cadran suivant le type de bouton.
Si le cadran et le bouton sont sur une même image, le bouton disparaît si je rends l'image invisible.
 
Top