IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaFX Discussion :

Slider circulaire (Slider Skinable)


Sujet :

JavaFX

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre à l'essai
    Homme Profil pro
    Inscrit en
    Janvier 2012
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Janvier 2012
    Messages : 5
    Par défaut Slider circulaire (Slider Skinable)
    Bonjour à tous,

    Je viens vers vous car j'ai un petit soucis.
    Pour un projet, je dois modéliser des boutons circulaire, du type bouton de volume de chaîne hifi. J'ai téléchargé les sources d'un exemple d'audio player disponible sur fxexperience.com (http://fxexperience.com/2012/01/fun-...-audio-player/). Le problème c'est que les boutons de volume et de balance sont fait en Slider et skinné ensuite. Mais le problème est que le code utilise une classe non disponible pour le grand public dans un package privé (com.sun.javafx.scene.control.skin.SkinBase).

    Du coup, est-ce qu'il y a moyen d'implémenter quelque chose de similaire mais en restant dans les API publics ? Si oui, comment ? Sinon comment faire un beau bouton rond avec Java FX ?

    Merci pour vos retours.
    Alex.

  2. #2
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 899
    Billets dans le blog
    54
    Par défaut
    En l'absence d'une possible surcharge de la skin, et même en utilisant CSS, une slider restera droite.

    Donc la ce que je conseille c'est de créer un composant custom dont l'interface (value, min, max) est inspire de Slider mais qui contient une ImageView (ou une Region skinnee par CSS) en interne et tournée a l'angle approprie.

    Voici un exemple code a la va-vite et probablement plein de bugs :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    /*
     * To change this template, choose Tools | Templates
     * and open the template in the editor.
     */
    package test;
     
    import java.text.MessageFormat;
    import javafx.beans.property.DoubleProperty;
    import javafx.beans.property.SimpleDoubleProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.event.EventHandler;
    import javafx.geometry.VPos;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.Region;
    import javafx.scene.layout.RegionBuilder;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Line;
    import javafx.scene.text.Text;
    import javafx.scene.transform.Rotate;
     
    /**
     * @author fabriceb
     */
    public class VolumeButton extends Region {
     
        private Region knob = RegionBuilder.create().id("knob").build(); // NOI18N.
        private final double minAngle = -20;
        private final double maxAngle = 200;
        private Rotate rotate = new Rotate();
        private Line currentLine = new Line();
        private Line minLine = new Line();
        private Line maxLine = new Line();
        private Text text = new Text();
     
        public VolumeButton() {
            super();
            getStyleClass().add("volume-button"); // NOI18N.
            knob.setPrefSize(75, 75);
            knob.getStyleClass().add("knob"); // NOI18N.
            knob.getTransforms().add(rotate);
            setOnMouseMoved(new EventHandler<MouseEvent>() {
     
                @Override
                public void handle(MouseEvent event) {
                    double x = event.getX();
                    double y = event.getY();
                    double centerX = getWidth() / 2.0;
                    double centerY = getHeight() / 2.0;
                    currentLine.setStartX(centerX);
                    currentLine.setStartY(centerY);
                    currentLine.setEndX(x);
                    currentLine.setEndY(y);
                }
            });
            setOnMouseDragged(new EventHandler<MouseEvent>() {
     
                @Override
                public void handle(MouseEvent event) {
                    double x = event.getX();
                    double y = event.getY();
                    double centerX = getWidth() / 2.0;
                    double centerY = getHeight() / 2.0;
                    currentLine.setStartX(centerX);
                    currentLine.setStartY(centerY);
                    currentLine.setEndX(x);
                    currentLine.setEndY(y);
                    double theta = Math.atan2((y - centerY), (x - centerX));
                    double angle = Math.toDegrees(theta);
                    if (angle > 0.0) {
                        angle = 180 + (180 - angle);
                    } else {
                        angle = 180 - (180 - Math.abs(angle));
                    }
                    if (angle >= 270) {
                        angle =  angle - 360;
                    }
                    double value = angleToValue(angle);
                    text.setText(MessageFormat.format("{0}\n{1}", angle, value));
                    setValue(value);
                }
            });
            minLine.setStroke(Color.GREEN);
            maxLine.setStroke(Color.BLUE);
            text.setTextOrigin(VPos.TOP);
            getChildren().addAll(minLine, maxLine);
            getChildren().add(knob);
            getChildren().addAll(currentLine, text);
            setPrefSize(100, 100);
            valueProperty().addListener(new ChangeListener<Number>() {
     
                @Override
                public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2) {
                    requestLayout();
                }
            });
            minProperty().addListener(new ChangeListener<Number>() {
     
                @Override
                public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2) {
                    requestLayout();
                }
            });
            maxProperty().addListener(new ChangeListener<Number>() {
     
                @Override
                public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2) {
                    requestLayout();
                }
            });
        }
     
        /**
         * {@inheritDoc}
         */
        @Override
        protected void layoutChildren() {
            super.layoutChildren();
            double centerX = getWidth() / 2.0;
            double centerY = getHeight() / 2.0;
            currentLine.setStartX(centerX);
            currentLine.setStartY(centerY);
            minLine.setStartX(centerX);
            minLine.setStartY(centerY);
            minLine.setEndX(centerX + 90 * Math.cos(Math.toRadians(-minAngle)));
            minLine.setEndY(centerY + 90 * Math.sin(Math.toRadians(-minAngle)));
            maxLine.setStartX(centerX);
            maxLine.setStartY(centerY);
            maxLine.setEndX(centerX + 90 * Math.cos(Math.toRadians(-maxAngle)));
            maxLine.setEndY(centerY + 90 * Math.sin(Math.toRadians(-maxAngle)));
            double knobX = (getWidth() - knob.getPrefWidth()) / 2.0;
            double knobY = (getHeight() - knob.getPrefHeight()) / 2.0;
            knob.setLayoutX(knobX);
            knob.setLayoutY(knobY);
            double value = getValue();
            double angle = valueToAngle(getValue());
            if (minAngle <= angle && angle <= maxAngle) {
                rotate.setPivotX(knob.getWidth() / 2.0);
                rotate.setPivotY(knob.getHeight() / 2.0);
                rotate.setAngle(-angle);
            }
        }
     
        double valueToAngle(double value) {
            double maxValue = getMax();
            double minValue = getMin();
            double angle = minAngle + (maxAngle - minAngle) * (value - minValue) / (maxValue - minValue);
            System.out.printf("valueToAngle %f => %f", value, angle).println();
            return angle;
        }
     
        double angleToValue(double angle) {
            double maxValue = getMax();
            double minValue = getMin();
            double value = minValue + (maxValue - minValue) * (angle - minAngle) / (maxAngle - minAngle);
            value = Math.max(minValue, value);
            value = Math.min(maxValue, value);
            System.out.printf("angleToValue %f => %f", angle, value).println();
            return value;
        }
        //
        private final DoubleProperty value = new SimpleDoubleProperty(this, "value", 0); // NOI18N.
     
        public final void setValue(double v) {
            value.set(v);
        }
     
        public final double getValue() {
            return value.get();
        }
     
        public final DoubleProperty valueProperty() {
            return value;
        }
        private final DoubleProperty min = new SimpleDoubleProperty(this, "min", 0); // NOI18N.
     
        public final void setMin(double v) {
            min.set(v);
        }
     
        public final double getMin() {
            return min.get();
        }
     
        public final DoubleProperty minProperty() {
            return min;
        }
        private final DoubleProperty max = new SimpleDoubleProperty(this, "max", 100); // NOI18N.
     
        public final void setMax(double v) {
            max.set(v);
        }
     
        public final double getMax() {
            return max.get();
        }
     
        public final DoubleProperty maxProperty() {
            return max;
        }
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    .volume-button {
        -fx-background-color: transparent;
    }
    .volume-button .knob {
        -fx-background-color: red;
        -fx-background-image: url("http://image.shutterstock.com/display_pic_with_logo/347911/347911,1251679404,1/stock-vector-volume-or-mute-icon-on-round-stainless-steel-modern-industrial-button-36190090.jpg");
        -fx-background-size: stretch; 
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    /*
     * To change this template, choose Tools | Templates
     * and open the template in the editor.
     */
    package test;
     
    import java.net.URL;
    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
     
    /**
     *
     * @author fabriceb
     */
    public class Main extends Application {
     
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            launch(args);
        }
     
        @Override
        public void start(Stage primaryStage) {
            System.setProperty("http.proxyHost", ...
            System.setProperty("http.proxyPort", ...
            ClassLoader loader = getClass().getClassLoader();
            VolumeButton volume = new VolumeButton();
            Scene scene = new Scene(volume);
            URL cssURL = loader.getResource("test/VolumeButton.css");
            if (cssURL == null) {
                cssURL = loader.getResource("test/VolumeButton.bss");
            }
            scene.getStylesheets().add(cssURL.toExternalForm());
            primaryStage.setTitle("Volume Button");
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    }
    Quelques remarques :
    • Les Line et le Text ne servent que pour le deboggage.
    • La syntaxe pour l'URI de l'image de -fx-background-image n'est pas claire dans la doc :
      Citation Envoyé par http://docs.oracle.com/javafx/2.0/api/javafx/scene/doc-files/cssref.html#region
      fx-background-image <uri> [ , <uri> ]* null A series of image URIs separated by commas.
      Citation Envoyé par http://docs.oracle.com/javafx/2.0/api/javafx/scene/doc-files/cssref.html#typeuri
      url ( [\"\']? <address> [\"\']? )

      <address> can be an absolute URI, for example:

      url(http://example.com)
      url('http://example.com')
      url("http://example.com")

      or it can be relative to the location of the CSS file.
      Mais je n'ai pas réussi a spécifier un fichier image qui soit dans le JAR/sur le CLASSPATH. Pour l'exemple j'ai pris une image quelconque sur le Net (d'ou le besoin de specifier le proxy dans mon cas).
    • Comme on peut le voir, le redimensionnement de l'image du bouton est assez degueulasse, mieux vaudra fournir une image de taille appropriee.


    Nom : Test.png
Affichages : 1009
Taille : 35,2 Ko
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  3. #3
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 899
    Billets dans le blog
    54
    Par défaut
    Correction, ca marche simplement avec une image locale en faisant ca:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    -fx-background-image: url("Knob.png");
    Pour un fichier image place dans le meme package que le CSS.
    Et dans ce cas plus besoin de specifier un proxy.

    Nom : Test.png
Affichages : 1129
Taille : 22,7 Ko
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  4. #4
    Membre très actif
    Avatar de la.lune
    Homme Profil pro
    Directeur Technique
    Inscrit en
    Décembre 2010
    Messages
    547
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Comores

    Informations professionnelles :
    Activité : Directeur Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2010
    Messages : 547
    Par défaut
    Bravo!! bouye
    Moi qui me tracasser la tête pour créer ça, mais de façon très null ,pas comme le votre.

  5. #5
    Membre très actif
    Avatar de la.lune
    Homme Profil pro
    Directeur Technique
    Inscrit en
    Décembre 2010
    Messages
    547
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Comores

    Informations professionnelles :
    Activité : Directeur Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2010
    Messages : 547
    Par défaut
    A vrai dire moi aussi ,j'ai un projet sur une simulation de phénomène électrique et il y a un galvanomètre avec ce bouton rond, du coup j'imaginais comment le créer,j'allais le faire avec un simple Cercle, dont un ImageView serait derrière, et le fait roter avec le rotate() et utilisant les touche vers le haut et vers bas, la souri aussi mais j’envisageais ça si tout marche bien, et pourquoi pas un évènement setOnScroll() affaire classé. Les min et les max là un peu de math pour qu'avec la souri on arrive pas à retourner de Max vers Min comme l'erreur que Jasper Potts l'a fait dans son demo de Audio Player. Avec le boutton volum c'est vrai les valeurs max et min sont bien respectés, mais si vous tenez la souri de max en voulant aller à l'autre sens ça va sauter vers Min. Alors que ça devait rester sur Max tant qu'on ne passe pas vers l'autre coté. La même chose se passe lorsque vous voulez passer de Min à Max. Pour la balance ,la souri ne tiens pas bien l'affaire mais avec les touche vers le haut et vers le bas ,les font bien bouger

    PS: Je pique votre code bouye

Discussions similaires

  1. [Slider/MediaPlayer] slider binding with reverse
    Par blackbird67 dans le forum JavaFX
    Réponses: 0
    Dernier message: 11/06/2010, 12h02
  2. Réponses: 1
    Dernier message: 02/06/2005, 19h23
  3. [MFC]Utilisation d'un SLIDER , problème de conversion
    Par sylvain_c42 dans le forum MFC
    Réponses: 1
    Dernier message: 10/05/2005, 14h31
  4. [MFC] Plusieurs sliders en un
    Par bigquick dans le forum MFC
    Réponses: 3
    Dernier message: 23/02/2005, 17h53
  5. affichage valeur d'un Slider
    Par djiwalloo dans le forum MFC
    Réponses: 4
    Dernier message: 24/11/2004, 10h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo