Cómo aplicar estilo a los componentes JavaFX usando un buen CSS antiguo.
Todas las publicaciones en la serie JavaFX:
- Tutorial JavaFX: Primeros pasos
- Tutorial JavaFX: ¡Hola mundo!
- Tutorial JavaFX: FXML y SceneBuilder
- Tutorial JavaFX: diseños básicos
- Tutorial JavaFX: diseños avanzados
- Tutorial JavaFX: estilo CSS
- JavaFX Weaver: integración de aplicaciones JavaFX y Spring Boot
Separación de elementos visuales.
En un
artículo anterior
sobre FXML, aprendimos cómo JavaFX proporciona una separación clara de tareas al dividir el código de la IU en dos. Los componentes y sus propiedades se declaran en el archivo FXML, y la lógica de interacción se asigna claramente al controlador.
Además, hay una tercera parte, el lenguaje FXML, que controla solo los componentes de su aplicación, sus propiedades y cómo están integrados entre sí. No define los elementos visuales de un componente, a saber: fuentes, colores, fondos, sangrías. En general, puede lograr esto en FXML, pero no debería. En cambio, los elementos visuales deben estar claramente definidos en las hojas de estilo CSS.
Por lo tanto, su diseño se vuelve independiente y se puede reemplazar o cambiar fácilmente sin afectar el resto de la aplicación. Incluso puede simplemente implementar varios temas que se pueden cambiar a petición del usuario.
CSS
Probablemente esté familiarizado con las CSS (hojas de estilo en cascada) utilizadas para diseñar páginas HTML en la web. Se implementa un enfoque similar en JavaFX, aunque JavaFX utiliza un conjunto de sus propias propiedades personalizadas.
Veamos un ejemplo:
.button { -fx-font-size: 15px; }
Aquí se utilizan dos conceptos principales. El primero es el selector de
botones . Determina a qué componentes se debe aplicar el estilo. En este ejemplo, el estilo se aplica a todos los botones.
La segunda parte son las propiedades reales del estilo, que se aplicarán a todos los componentes que coincidan con nuestro selector. Las propiedades son todo dentro de llaves.
Cada propiedad tiene un significado específico. En nuestro ejemplo, existe la propiedad
-fx-font-size , que determina qué tan grande será el texto. En el ejemplo, el valor es
15px , pero este valor puede ser cualquier otro.
Para resumir: creamos una regla que establece que todos los botones en todas partes deben tener texto de 15 píxeles.
Selectores
Ahora echemos un vistazo más de cerca a cómo funcionan los selectores en JavaFX. Esto sucede casi lo mismo que en CSS normal.
Clase
Una clase en CSS representa varios elementos similares. Por ejemplo, botones o casillas de verificación. El selector, que debe aplicarse a todos los elementos de la misma clase, comienza con un punto ".", Seguido inmediatamente por el nombre de la clase. Una convención de nomenclatura de clase consiste en separar palabras individuales con el carácter "-". El siguiente selector se aplica a todos los elementos con la clase de
etiqueta .
.label { // Some properties }
Clases integradas
La buena noticia es que todos los componentes integrados de JavaFX (como Label o Button) ya tienen una clase predefinida. Si desea personalizar el estilo de todas las etiquetas en su aplicación, no necesita agregar ninguna clase personalizada para cada una de sus etiquetas. Cada etiqueta tiene una clase de
etiqueta por defecto.
Es fácil determinar el nombre de clase del componente:
- Tome el nombre de la clase de componente Java, por ejemplo. Etiqueta
- Poner el nombre en minúsculas
- Si consta de varias palabras, sepárelas con el símbolo "-"
Algunos ejemplos:
- Etiqueta → Etiqueta
- Casilla de verificación → casilla de verificación
Cuando use clases como selectores, asegúrese de agregar ".". Esto significa que el selector para la clase de
etiqueta es
.label .
Clases personalizadas
Si las clases integradas no son suficientes, puede agregar sus propias clases a sus componentes. Puede usar varias clases separadas por comas:
<Label styleClass="my-label,other-class">I am a simple label</Label>
O en Java:
Label label = new Label("I am a simple label"); label.getStyleClass().addAll("my-label", "other-class");
Agregar clases de esta manera no elimina la clase de componente de forma predeterminada (en este caso,
etiqueta ).
Hay una clase especial llamada
raíz . Es el componente raíz de su escena. Puede usarlo para diseñar todo dentro de su escena (por ejemplo, establecer una fuente global). Esto es similar a usar el selector de etiqueta de cuerpo en HTML.
ID
Otra forma de seleccionar componentes en CSS es usar un identificador de componente (ID). Es un identificador único para un componente. A diferencia de las clases que pueden asignarse a múltiples componentes, el identificador debe ser único en la escena.
Mientras que el símbolo "." Se utiliza para indicar la clase. delante del nombre en sus selectores, los identificadores están marcados con el símbolo "#".
#my-component { ... }
En FXML, puede usar
fx: id para establecer el identificador CSS de un componente.
<Label fx:id="foo">I am a simple label</Label>
Sin embargo, hay una advertencia. El mismo
identificador se utiliza para referirse al objeto componente declarado en su controlador con el mismo nombre. Dado que el identificador y el nombre del campo en el controlador deben coincidir,
fx: id debe tener en cuenta la restricción de nomenclatura de Java para los nombres de campo. Aunque la convención de nomenclatura CSS define palabras individuales separadas por un carácter "-", es un carácter no válido para los nombres de campo Java. Por lo tanto, para
fx: id con algunas palabras, debe usar una convención de nomenclatura diferente, como CamelCase, o usar subrayado.
<Label fx:id="my-label">I am a simple label</Label> <Label fx:id="my_label">I am a simple label</Label> <Label fx:id="MyLabel">I am a simple label</Label>
En Java, simplemente puede llamar al método
setId () de su componente.
Label label = new Label("I am a simple label"); label.setId("foo");
Propiedades
Aunque el CSS utilizado en JavaFX es muy similar al CSS web original, hay una gran diferencia. Los nombres de propiedad son diferentes y hay muchas propiedades nuevas específicas de JavaFX. Tienen el prefijo
-fx- .
Aquí hay algunos ejemplos:
- -fx-background-color : color de fondo
- -fx-text-fill : color del texto
- -fx-font-size : tamaño del texto
Puede encontrar una lista de todas las propiedades en la
guía de diseño oficial .
Pseudo-clases
Además de las clases habituales que marcan componentes específicos, existen las llamadas pseudo-clases que indican el estado de un componente. Esto podría ser, por ejemplo, una clase para marcar que el componente tiene el foco o que el cursor del mouse está sobre él.
Hay muchas pseudo-clases incorporadas. Miremos el botón. Hay varias pseudo-clases que puede usar, por ejemplo:
- pasar el mouse: pasar el mouse sobre el botón
- enfocado : el botón tiene foco
- deshabilitado : el botón está deshabilitado
- presionado : botón presionado
Las pseudo-clases comienzan con el carácter ":" (por ejemplo
:: hover ) en los selectores CSS. Por supuesto, debe especificar a qué componente pertenece su pseudoclase, por ejemplo,
botón: desplazarse . El siguiente ejemplo muestra un selector que se aplica a todos los botones que tienen foco:
.button:focused { -fx-background-color: red; }
A diferencia de CSS, que solo tiene pseudo-clases básicas para estados como
focus y
hover , JavaFX tiene pseudo-clases específicas de componentes que se relacionan con diferentes estados o propiedades de componentes.
Por ejemplo:
- Las barras de desplazamiento tienen pseudo-clases horizontales y verticales.
- Los elementos (celdas) tienen pseudo-clases pares e impares
- TitledPane ha expandido y colapsado las pseudo-clases.
Pseudo-clases personalizadas
Además de las pseudo-clases incorporadas, puede definir y usar sus propias pseudo-clases.
Creemos nuestra propia etiqueta (heredando de la clase Label). Tendrá una nueva propiedad lógica llamada
brillante . En este caso, queremos que nuestra etiqueta tenga una pseudoclase
brillante .
Como la etiqueta tiene una pseudoclase
brillante , podemos establecer el fondo de la etiqueta
dorada :
.shiny-label:shiny { -fx-background-color: gold; }
Ahora crea la clase misma.
public class ShinyLabel extends Label { private BooleanProperty shiny; public ShinyLabel() { getStyleClass().add("shiny-label"); shiny = new SimpleBooleanProperty(false); shiny.addListener(e -> { pseudoClassStateChanged(PseudoClass.getPseudoClass("shiny"), shiny.get()); }); } public boolean isShiny() { return shiny.get(); } public void setShiny(boolean shiny) { this.shiny.set(shiny); } }
Hay varias partes importantes aquí:
- Tenemos la propiedad booleana BooleanProperty en lugar de la booleana habitual. Esto significa que el objeto brillante es observable, y podemos rastrear (escuchar) los cambios en su valor.
- Registramos un oyente que se llamará cada vez que cambie el valor del objeto brillante usando shiny.addListener () .
- Cuando el valor brillante cambia, agregamos / eliminamos la pseudoclase brillante dependiendo del valor actual de pseudoClassStateChanged (PseudoClass.getPseudoClass ("shiny"), shiny.get ()) .
- Agregamos una clase personalizada para todas las etiquetas de etiqueta brillante , en lugar de tener solo la clase de etiqueta heredada del padre. Por lo tanto, solo podemos seleccionar etiquetas brillantes .
Hoja de estilo predeterminada
Incluso si usted no proporciona ningún estilo, cada aplicación JavaFX ya tiene algunos estilos visuales. Hay una hoja de estilo predeterminada que se aplica a todas las aplicaciones. Se llama
modena (desde JavaFX 8, anteriormente se llamaba
caspian ).
Esta hoja de estilo se puede encontrar en el archivo:
jfxrt.jar \ com \ sun \ javafx \ scene \ control \ skin \ modena \ modena.cssO puede encontrar el archivo
aquí . En el mismo directorio hay muchas imágenes utilizadas por la hoja de estilo.
Esta hoja de estilo proporciona estilos predeterminados, pero tiene la prioridad más baja sobre otros tipos de hojas de estilo, por lo que puede anularla fácilmente.
Hoja de estilo de escena
Además de la hoja de estilo predeterminada mencionada anteriormente, por supuesto, puede proporcionar la suya. El nivel más alto en el que puede aplicar estilización es toda la escena. Puede implementar esto en su FXML:
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" stylesheets="styles.css" ... > ... </BorderPane>
O en tu código Java:
String stylesheet = getClass().getResource("/styles.css").toExternalForm(); scene.getStylesheets().add(stylesheet);
Presta atención a la llamada a
ExternalForm () . Scene espera obtener el contenido de la hoja de estilo como una cadena, no como un archivo, por lo que debemos proporcionar el contenido de nuestra hoja de estilo como una cadena.
Hoja de estilo principal
Además de la hoja de estilo para toda la escena, a veces es útil tener estilos en el nivel de diseño. Es decir, para un contenedor separado, como VBox, HBox o GridPane. El padre común de todos los diseños es la clase padre, que define métodos para procesar hojas de estilo a nivel de diseño. Estos estilos se aplican solo a los componentes de este diseño, y no a toda la escena. Un estilo en el nivel de diseño tiene prioridad sobre un estilo en el nivel de escena.
<HBox stylesheets="styles.css"> ... </HBox>
En Java, debe cargar el contenido de la hoja de estilo usted mismo, como antes para la escena:
HBox box = new HBox(); String stylesheet = getClass().getResource("/styles.css").toExternalForm(); box.getStylesheets().add(stylesheet);
Estilos en línea
Hasta ahora, solo hemos visto casos en los que se ha asignado una hoja de estilo externa a una escena o diseño completo. Pero puede establecer propiedades de estilo individuales en el nivel de componente.
Aquí no necesita preocuparse por el selector, ya que todas las propiedades están configuradas para un componente específico.
Puede especificar varias propiedades separadas por punto y coma:
<Label style="-fx-background-color: blue; -fx-text-fill: white"> I'm feeling blue. </Label>
En Java, puede usar el método
setStyle () :
Label label = new Label("I'm feeling blue."); label.setStyle("-fx-background-color: blue; -fx-text-fill: white");
Los estilos en el nivel de componente tienen prioridad sobre los estilos de escena, así como los estilos principales en el nivel de diseño.
¿Por qué necesitas evitarlos?
El estilo a nivel de componente puede ser conveniente, pero es una solución rápida y sucia. Estás abandonando la principal ventaja de CSS, que es la separación de estilos de componentes. Ahora vincula rígidamente sus elementos visuales directamente a los componentes. Ya no puede cambiar fácilmente sus hojas de estilo cuando sea necesario, no puede cambiar los temas.
Además, ya no tiene un solo lugar central donde se define su estilo. Cuando necesita cambiar algo en un conjunto de componentes similares, debe cambiar cada uno de los componentes por separado y no editar solo un lugar en la hoja de estilo externa. Por lo tanto, se deben evitar los estilos de componentes en línea.
Prioridades de la hoja de estilo
Puede proporcionar estilos en varios niveles: escena, padre, estilos en línea, y también hay una hoja de estilo de módem predeterminada. Si cambia la misma propiedad del mismo componente en varios niveles, JavaFX tiene una configuración de prioridad que determina qué estilos deben usarse. Lista de prioridades: de mayor a menor:
- Estilos en línea
- Estilos parentales
- Estilos de escena
- Estilos predeterminados
Esto significa que si establece el color de fondo de una determinada etiqueta tanto en el nivel en línea como en el de escena, JavaFX utilizará el valor establecido en los estilos en línea porque tiene una prioridad más alta.
Lectura adicional
JavaFX tiene muchas propiedades CSS, y su descripción está más allá del alcance de esta publicación; para obtener una lista detallada, consulte la
guía oficial de referencia CSS para JavaFX .