Developpez.com

Le Club des Développeurs et IT Pro

CSS et JavaFx : un développeur affirme pouvoir reproduire l'UI de tous les OS

Un défi pour montrer le potentiel de ces technologies

Le 2011-12-21 15:29:15, par la.lune, Membre chevronné
Des gens demandaient à Jasper Potts (Architecte Développeur pour JavaFX) s'il était possible de recréer le look de tel ou tel OS avec JavaFX 2.x. Certains, persuadés qu'il ne pourrait pas, lui ont même lancé un défi.

Il a donc décidé de le relever.

La clef de sa méthode repose sur les feuilles de styles (CSS) qui permettent de personnaliser les boutons de JavaFX.

Résultat, des UI d'iPad, d'iPhone, de Windows 7, ou d'Android sans utiliser la moindre image (un design faisable également sans CSS et rien qu'en code Java avec les API JavaFX 2.x).

La meilleure méthode reste cependant les feuilles de style qui séparent le contenu et le rendu graphique. Et qui sont plus simple à coder qu'avec du Java.



Dans le détail, Jasper Potts explique qu'il utilise les gradients du CSS. Un bouton d'UI aura un air de Windows 7 si on lui applique par exemple la feuille de style suivante :

Code :
1
2
3
4
5
6
7
8
9
10
11
#windows7-default {
    -fx-background-color:
        #3c7fb1,
        linear-gradient(#fafdfe, #e8f5fc),
        linear-gradient(#eaf6fd 0%, #d9f0fc 49%, #bee6fd 50%, #a7d9f5 100%);
    -fx-background-insets: 0,1,2;
    -fx-background-radius: 3,2,1;
    -fx-padding: 3 30 3 30;
    -fx-text-fill: black;
    -fx-font-size: 14px;
}
Défi réussi ?

Le projet Netbeans "ButtonStyles.zip" est disponible en téléchargement libre

Source : Billet de Jasper Potts
  Discussion forum
13 commentaires
  • sinok
    Expert éminent sénior
    Envoyé par huit_six
    Du coup, je comprend pas trop quel est le lien avec JavaFX, si c'est du CSS ?
    C'est pas un troll hein ! c'est une vrai question vu que je connais pas javafx !
    JavaFX permet le theming de ses composants via des feuilles de style CSS, comme d'autres API le permettent (genre Qt & cie).
  • la.lune
    Membre chevronné
    En utilisant le patern Build fournit aussi dans l'api JavaFX 2.x ,facile et rapide ,il a coder l'application comme suit:

    Code :
    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
     
    package buttonstyles;
    
    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.SceneBuilder;
    import javafx.scene.control.ButtonBuilder;
    import javafx.scene.control.ToolBarBuilder;
    import javafx.scene.layout.BorderPaneBuilder;
    import javafx.scene.layout.VBoxBuilder;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    
    /**
     * @author Jasper
     */
    public class ButtonStyles extends Application {
    
        public static void main(String[] args) { launch(args); }
        
        @Override public void start(Stage primaryStage) {
            primaryStage.setTitle("JavaFX CSS Buttons");
            primaryStage.setScene(SceneBuilder.create()             
                .stylesheets(ButtonStyles.class.getResource("Buttons.css").toExternalForm())
                .fill(Color.gray(0.9))
                .root(
                    BorderPaneBuilder.create()
                        .left(
                            VBoxBuilder.create()
                                .spacing(10)
                                .padding(new Insets(20))
                                .alignment(Pos.CENTER)
                                .children(
                                    ButtonBuilder.create().text("Green").id("green").build(),
                                    ButtonBuilder.create().text("Round Red").id("round-red").build(),
                                    ButtonBuilder.create().text("Bevel Grey").id("bevel-grey").build(),
                                    ButtonBuilder.create().text("Glass Grey").id("glass-grey").build(),
                                    ButtonBuilder.create().text("Shiny Orange").id("shiny-orange").build(),
                                    ButtonBuilder.create().text("DARK BLUE").id("dark-blue").build(),
                                    ButtonBuilder.create().text("Record Sales").id("record-sales").build(),
                                    ButtonBuilder.create().text("Rich Blue").id("rich-blue").build(),
                                    ButtonBuilder.create().text("Big Yellow").id("big-yellow").build(),
                                    ToolBarBuilder.create()
                                        .id("iphone-toolbar")
                                        .items(
                                            ButtonBuilder.create().text("iPhone").id("iphone").build()
                                        )
                                        .build(),
                                    ButtonBuilder.create().text("Large iPad Dark Grey").id("ipad-dark-grey").build(),
                                    ButtonBuilder.create().text("Large iPad Grey").id("ipad-grey").build(),
                                    ButtonBuilder.create().text("OSX Lion (Default)").id("lion-default").build(),
                                    ButtonBuilder.create().text("OSX Lion").id("lion").build(),
                                    ButtonBuilder.create().text("Windows 7 (Default)").id("windows7-default").build(),
                                    ButtonBuilder.create().text("Windows 7").id("windows7").build()
                                )
                                .build()
                        )
                        .center(
                            VBoxBuilder.create()
                                .spacing(10)
                                .padding(new Insets(20))
                                .alignment(Pos.CENTER)
                                .style("-fx-background-color: #373737;")
                                .children(
                                    ButtonBuilder.create().text("Green").id("green").build(),
                                    ButtonBuilder.create().text("Round Red").id("round-red").build(),
                                    ButtonBuilder.create().text("Bevel Grey").id("bevel-grey").build(),
                                    ButtonBuilder.create().text("Glass Grey").id("glass-grey").build(),
                                    ButtonBuilder.create().text("Shiny Orange").id("shiny-orange").build(),
                                    ButtonBuilder.create().text("DARK BLUE").id("dark-blue").build(),
                                    ButtonBuilder.create().text("Record Sales").id("record-sales").build(),
                                    ButtonBuilder.create().text("Rich Blue").id("rich-blue").build(),
                                    ButtonBuilder.create().text("Big Yellow").id("big-yellow").build(),
                                    ToolBarBuilder.create()
                                        .id("iphone-toolbar")
                                        .items(
                                            ButtonBuilder.create().text("iPhone").id("iphone").build()
                                        )
                                        .build(),
                                    ButtonBuilder.create().text("Large iPad Dark Grey").id("ipad-dark-grey").build(),
                                    ButtonBuilder.create().text("Large iPad Grey").id("ipad-grey").build(),
                                    ButtonBuilder.create().text("OSX Lion (Default)").id("lion-default").build(),
                                    ButtonBuilder.create().text("OSX Lion").id("lion").build(),
                                    ButtonBuilder.create().text("Windows 7 (Default)").id("windows7-default").build(),
                                    ButtonBuilder.create().text("Windows 7").id("windows7").build()
                                )
                                .build()
                        )
                    .build()
                )
                .build());
            primaryStage.show();
        }
    }
    A noter qu'ici il s'est suffit de l'id du contrôle pour le référencer dans les feuilles de style,mais il est possible d'utiliser les class
    Code :
    buton0.setStylClass(String)
    . De plus il est possible d’intégrer directement le style en appelant la méthode du contrôle comme ça:

    Code :
    button1.setStyle("-fx-font: 22 arial; -fx-base: #b6e7c9;");
    Vous trouverez un bon document mais en anglais qui explique comment créer et gérer les contrôles,leurs effets et styles,effets 3D.ici
  • tchize_
    Expert éminent sénior
    Autant java fx 1.0 était imbuvable, autant le faite que javafx2 interagisse avec du code java sauve un peu le miche. Et il faut le reconnaitre. aujourd'hui, c'est la seul api "officielle" de oracle foutue de fournir des interfaces graphique présentables

    Bon après, pour le moment coté interface graphique, je préfère encore apache pivot, mais qui sait, avec java 8, ....
  • bouye
    Rédacteur/Modérateur
    Sort la tête de ses vacances sans programmation...

    Ah donc ils a continué sur ce sujet. Qqun lui avait posé la question lors de la session "Meet the Java & JavaFX Experience Team" car il était dans l'audience en tant que guest spectator (même si au final plus de 50% des questions lui ont été posée à lui au lieu des speakers) et il indiquait effectivement que le CSS était une bonne voie pour avoir un LnF natif. Ce à quoi on avait rétorqué que ça serait bien qu'Oracle fournisse ces CSS de la même manière qu'ils fournissent déjà des LnF natifs pour Swing.

    Une question similaire avait été postée lors de la première présentation de Jim Weaver et aussi lors d'une autre session ; il semblait qu'un frein majeur pour la pénétration de FX en entreprise fusse le fait que Caspian est trop différent du LnF système.

    L'ennuie avec le CSS est que de nos jours la plupart des LnF sont animés (boutons qui pulsent etc.), hors les CSS de FX ne supportent pas les animations (pour le moment).

    Concernant Swing, oui on peut faire des jolis truc mais par défaut il faut presque recoder tous les composants (ou du moins toutes leurs UI). Ou utiliser Substance, chose que Kiril a mis des années et des années à developper. Là, en JavaFX ,c'est beaucoup plus rapide, faut juste modifier le CSS et relancer le programme.

    Retourne profiter de ses derniers jours de vacances.
    Ah oui et bonne année au fait !
  • la.lune
    Membre chevronné
    Voici le code du fichier CSS très simple à intégrer dans une sceneGraph JavaFX
    Code :
    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
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    #green {
        -fx-background-color:
            linear-gradient(#f0ff35, #a9ff00),
            radial-gradient(center 50% -40%, radius 200%, #b8ee36 45%, #80c800 50%);
        -fx-background-radius: 6, 5;
        -fx-background-insets: 0, 1;
        -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.4) , 5, 0.0 , 0 , 1 );
        -fx-text-fill: #395306;
    }
    #round-red {
        -fx-background-color: linear-gradient(#ff5400, #be1d00);
        -fx-background-radius: 30;
        -fx-background-insets: 0;
        -fx-text-fill: white;
    }
    #bevel-grey {
        -fx-background-color:
            linear-gradient(#f2f2f2, #d6d6d6),
            linear-gradient(#fcfcfc 0%, #d9d9d9 20%, #d6d6d6 100%),
            linear-gradient(#dddddd 0%, #f6f6f6 50%);
        -fx-background-radius: 8,7,6;
        -fx-background-insets: 0,1,2;
        -fx-text-fill: black;
        -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 );
    }
    #glass-grey {
        -fx-background-color:
            #c3c4c4,
            linear-gradient(#d6d6d6 50%, white 100%),
            radial-gradient(center 50% -40%, radius 200%, #e6e6e6 45%, rgba(230,230,230,0) 50%);
        -fx-background-radius: 30;
        -fx-background-insets: 0,1,1;
        -fx-text-fill: black;
        -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 3, 0.0 , 0 , 1 );
    }
    #shiny-orange {
        -fx-background-color:
            linear-gradient(#ffd65b, #e68400),
            linear-gradient(#ffef84, #f2ba44),
            linear-gradient(#ffea6a, #efaa22),
            linear-gradient(#ffe657 0%, #f8c202 50%, #eea10b 100%),
            linear-gradient(from 0% 0% to 15% 50%, rgba(255,255,255,0.9), rgba(255,255,255,0));
        -fx-background-radius: 30;
        -fx-background-insets: 0,1,2,3,0;
        -fx-text-fill: #654b00;
        -fx-font-weight: bold;
        -fx-font-size: 14px;
        -fx-padding: 10 20 10 20;
    }
    #dark-blue {
        -fx-background-color:
            #090a0c,
            linear-gradient(#38424b 0%, #1f2429 20%, #191d22 100%),
            linear-gradient(#20262b, #191d22),
            radial-gradient(center 50% 0%, radius 100%, rgba(114,131,148,0.9), rgba(255,255,255,0));
        -fx-background-radius: 5,4,3,5;
        -fx-background-insets: 0,1,2,0;
        -fx-text-fill: white;
        -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 5, 0.0 , 0 , 1 );
        -fx-font-family: "Arial";
        -fx-text-fill: linear-gradient(white, #d0d0d0);
        -fx-font-size: 12px;
        -fx-padding: 10 20 10 20;
    }
    #dark-blue Text {
        -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.9) , 1, 0.0 , 0 , 1 );
    }
    #record-sales {
        -fx-padding: 8 15 15 15;
        -fx-background-insets: 0,0 0 5 0, 0 0 6 0, 0 0 7 0;
        -fx-background-radius: 8;
        -fx-background-color:
            linear-gradient(from 0% 93% to 0% 100%, #a34313 0%, #903b12 100%),
            #9d4024,
            #d86e3a,
            radial-gradient(center 50% 50%, radius 100%, #d86e3a, #c54e2c);
        -fx-effect: dropshadow( gaussian , rgba(0,0,0,0.75) , 4,0,0,1 );
        -fx-font-weight: bold;
        -fx-font-size: 1.1em;
    }
    #record-sales:hover {
        -fx-background-color:
            linear-gradient(from 0% 93% to 0% 100%, #a34313 0%, #903b12 100%),
            #9d4024,
            #d86e3a,
            radial-gradient(center 50% 50%, radius 100%, #ea7f4b, #c54e2c);
    }
    #record-sales:pressed {
        -fx-padding: 10 15 13 15;
        -fx-background-insets: 2 0 0 0,2 0 3 0, 2 0 4 0, 2 0 5 0;
    }
    #record-sales Text {
        -fx-fill: white;
        -fx-effect: dropshadow( gaussian , #a30000 , 0,0,0,2 );
    }
    #rich-blue {
        -fx-background-color:
            #000000,
            linear-gradient(#7ebcea, #2f4b8f),
            linear-gradient(#426ab7, #263e75),
            linear-gradient(#395cab, #223768);
        -fx-background-insets: 0,1,2,3;
        -fx-background-radius: 3,2,2,2;
        -fx-padding: 12 30 12 30;
        -fx-text-fill: white;
        -fx-font-size: 12px;
    }
    #rich-blue Text {
        -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.8) , 0, 0.0 , 0 , 1);
    }
    #big-yellow {
        -fx-background-color:
            #ecebe9,
            rgba(0,0,0,0.05),
            linear-gradient(#dcca8a, #c7a740),
            linear-gradient(#f9f2d6 0%, #f4e5bc 20%, #e6c75d 80%, #e2c045 100%),
            linear-gradient(#f6ebbe, #e6c34d);
        -fx-background-insets: 0,9 9 8 9,9,10,11;
        -fx-background-radius: 50;
        -fx-padding: 15 30 15 30;
        -fx-font-family: "Helvetica";
        -fx-font-size: 18px;
        -fx-text-fill: #311c09;
        -fx-effect: innershadow( three-pass-box , rgba(0,0,0,0.1) , 2, 0.0 , 0 , 1);
    }
    #big-yellow Text {
        -fx-effect: dropshadow( one-pass-box , rgba(255,255,255,0.5) , 0, 0.0 , 0 , 1);
    }
    #iphone-toolbar {
        -fx-background-color: linear-gradient(#98a8bd 0%, #8195af 25%, #6d86a4 100%);
    }
    #iphone {
        -fx-background-color:
            #a6b5c9,
            linear-gradient(#303842 0%, #3e5577 20%, #375074 100%),
            linear-gradient(#768aa5 0%, #849cbb 5%, #5877a2 50%, #486a9a 51%, #4a6c9b 100%);
        -fx-background-insets: 0 0 -1 0,0,1;
        -fx-background-radius: 5,5,4;
        -fx-padding: 7 30 7 30;
        -fx-text-fill: #242d35;
        -fx-font-family: "Helvetica";
        -fx-font-size: 12px;
        -fx-text-fill: white;
    }
    #iphone Text {
        -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.8) , 0, 0.0 , 0 , -1 );
    }
    #ipad-dark-grey {
        -fx-background-color:
            linear-gradient(#686868 0%, #232723 25%, #373837 75%, #757575 100%),
            linear-gradient(#020b02, #3a3a3a),
            linear-gradient(#9d9e9d 0%, #6b6a6b 20%, #343534 80%, #242424 100%),
            linear-gradient(#8a8a8a 0%, #6b6a6b 20%, #343534 80%, #262626 100%),
            linear-gradient(#777777 0%, #606060 50%, #505250 51%, #2a2b2a 100%);
        -fx-background-insets: 0,1,4,5,6;
        -fx-background-radius: 9,8,5,4,3;
        -fx-padding: 15 30 15 30;
        -fx-font-family: "Helvetica";
        -fx-font-size: 18px;
        -fx-font-weight: bold;
        -fx-text-fill: white;
        -fx-effect: dropshadow( three-pass-box , rgba(255,255,255,0.2) , 1, 0.0 , 0 , 1);
    }
    #ipad-dark-grey Text {
        -fx-effect: dropshadow( one-pass-box , black , 0, 0.0 , 0 , -1 );
    }
    #ipad-grey {
        -fx-background-color:
            linear-gradient(#686868 0%, #232723 25%, #373837 75%, #757575 100%),
            linear-gradient(#020b02, #3a3a3a),
            linear-gradient(#b9b9b9 0%, #c2c2c2 20%, #afafaf 80%, #c8c8c8 100%),
            linear-gradient(#f5f5f5 0%, #dbdbdb 50%, #cacaca 51%, #d7d7d7 100%);
        -fx-background-insets: 0,1,4,5;
        -fx-background-radius: 9,8,5,4;
        -fx-padding: 15 30 15 30;
        -fx-font-family: "Helvetica";
        -fx-font-size: 18px;
        -fx-font-weight: bold;
        -fx-text-fill: #333333;
        -fx-effect: dropshadow( three-pass-box , rgba(255,255,255,0.2) , 1, 0.0 , 0 , 1);
    }
    #ipad-grey Text {
        -fx-effect: dropshadow( one-pass-box , white , 0, 0.0 , 0 , 1 );
    }
    #lion-default {
        -fx-background-color:
            rgba(0,0,0,0.08),
            linear-gradient(#5a61af, #51536d),
            linear-gradient(#e4fbff 0%,#cee6fb 10%, #a5d3fb 50%, #88c6fb 51%, #d5faff 100%);
        -fx-background-insets: 0 0 -1 0,0,1;
        -fx-background-radius: 5,5,4;
        -fx-padding: 3 30 3 30;
        -fx-text-fill: #242d35;
        -fx-font-size: 14px;
    }
    #lion {
        -fx-background-color:
            rgba(0,0,0,0.08),
            linear-gradient(#9a9a9a, #909090),
            linear-gradient(white 0%, #f3f3f3 50%, #ececec 51%, #f2f2f2 100%);
        -fx-background-insets: 0 0 -1 0,0,1;
        -fx-background-radius: 5,5,4;
        -fx-padding: 3 30 3 30;
        -fx-text-fill: #242d35;
        -fx-font-size: 14px;
    }
    #windows7-default {
        -fx-background-color:
            #3c7fb1,
            linear-gradient(#fafdfe, #e8f5fc),
            linear-gradient(#eaf6fd 0%, #d9f0fc 49%, #bee6fd 50%, #a7d9f5 100%);
        -fx-background-insets: 0,1,2;
        -fx-background-radius: 3,2,1;
        -fx-padding: 3 30 3 30;
        -fx-text-fill: black;
        -fx-font-size: 14px;
    }
    #windows7 {
        -fx-background-color:
            #707070,
            linear-gradient(#fcfcfc, #f3f3f3),
            linear-gradient(#f2f2f2 0%, #ebebeb 49%, #dddddd 50%, #cfcfcf 100%);
        -fx-background-insets: 0,1,2;
        -fx-background-radius: 3,2,1;
        -fx-padding: 3 30 3 30;
        -fx-text-fill: black;
        -fx-font-size: 14px;
    }
  • huit_six
    Membre actif
    Du coup, je comprend pas trop quel est le lien avec JavaFX, si c'est du CSS ?
    C'est pas un troll hein ! c'est une vrai question vu que je connais pas javafx !
  • la.lune
    Membre chevronné
    Envoyé par huit_six

    C'est pas un troll hein !
    Et bien ce n'est pas un troll dans le moment où on t'a donné le code source, la source de l'info et un lien pour télécharger le projet Netbeans .Donc si tu télécharges La 2e mise à jour du JDK 7: JDK 7u2. Le SDK de JavaFX 2.0.2 est inclut et s’installe automatiquement après installation du JDK.Tu pourra voir toi même le résultat.

    A présent une version de la version 2.1 est disponible pour Windows et Mac Os
    Téléchargez JavaFX 2.1 b06
  • la.lune
    Membre chevronné
    Envoyé par tchize_
    Et il faut le reconnaitre. aujourd'hui, c'est la seul api "officielle" de oracle foutue de fournir des interfaces graphique présentables
    Mais on peut faire du présentable avec Swing non? Et oui regarde un peu ça!





    C'est du Swing mon ami, et bien tu peux trouver ce thème agréable sans le site java.net par ici
  • tchize_
    Expert éminent sénior
    Je parlais surtout du niveau binding entre les éléments, l'aspect dynamique des interfaces etc. Swing, même avec un beau toolkit, ça reste swing avec sa complexité et ses limitations.
  • la.lune
    Membre chevronné
    Envoyé par tchize_
    Je parlais surtout du niveau binding entre les éléments, l'aspect dynamique des interfaces etc. Swing, même avec un beau toolkit, ça reste swing avec sa complexité et ses limitations.
    Je confirme! Tout reste que JavaFX gère bien tout ce que tu viens de mentionner et plus!