Como estilizar componentes JavaFX usando o bom e velho CSS.
Todas as postagens na série JavaFX:
- Tutorial JavaFX: Introdução
- Tutorial JavaFX: Olá, mundo!
- Tutorial JavaFX: FXML e SceneBuilder
- Tutorial JavaFX: layouts básicos
- Tutorial JavaFX: layouts avançados
- Tutorial JavaFX: estilo CSS
- JavaFX Weaver: Integrando aplicativos JavaFX e Spring Boot
Separação de elementos visuais
Em um
artigo anterior
sobre FXML, aprendemos como o JavaFX fornece uma separação clara de tarefas dividindo o código da interface do usuário em duas. Os componentes e suas propriedades são declarados no arquivo FXML e a lógica de interação é claramente alocada ao controlador.
Além disso, há uma terceira parte, a linguagem FXML, que controla apenas os componentes do seu aplicativo, suas propriedades e como eles são incorporados um ao outro. Ele não define os elementos visuais de um componente, a saber: fontes, cores, planos de fundo, recuos. Em geral, você pode conseguir isso em FXML, mas não deve. Em vez disso, os elementos visuais devem ser claramente definidos nas folhas de estilo CSS.
Assim, seu design se torna independente e pode ser facilmente substituído ou alterado sem afetar o restante do aplicativo. Você pode simplesmente implementar vários temas que podem ser alterados a pedido do usuário.
CSS
Você provavelmente conhece o CSS (Cascading Style Sheets) usado para estilizar páginas HTML na web. Uma abordagem semelhante é implementada no JavaFX, embora o JavaFX use um conjunto de suas próprias propriedades customizadas.
Vejamos um exemplo:
.button { -fx-font-size: 15px; }
Dois conceitos principais são usados aqui. O primeiro é o seletor de botão. Determina a quais componentes o estilo deve ser aplicado. Neste exemplo, o estilo é aplicado a todos os botões.
A segunda parte são as propriedades reais do estilo, que serão aplicadas a todos os componentes que correspondem ao nosso seletor. Propriedades são tudo dentro de chaves.
Cada propriedade tem um significado específico. No nosso exemplo, existe a propriedade
-fx-font-size , que determina o tamanho do texto. No exemplo, o valor é
15px , mas esse valor pode ser qualquer outro.
Para resumir - criamos uma regra que afirma que todos os botões em todos os lugares devem ter texto de 15 pixels.
Seletores
Agora, vamos dar uma olhada em como os seletores funcionam no JavaFX. Isso acontece quase da mesma forma que no CSS comum.
Class
Uma classe em CSS representa vários elementos semelhantes. Por exemplo, botões ou caixas de seleção. O seletor, que deve ser aplicado a todos os elementos da mesma classe, começa com um ponto ".", Seguido imediatamente pelo nome da classe. Uma convenção de nomenclatura de classe é separar palavras individuais com o caractere "-". O seletor a seguir se aplica a todos os elementos com a classe
label .
.label { // Some properties }
Classes incorporadas
A boa notícia é que todos os componentes internos do JavaFX (como Label ou Button) já possuem uma classe predefinida. Se você deseja personalizar o estilo de todos os rótulos no seu aplicativo, não é necessário adicionar nenhuma classe personalizada para cada um deles. Cada etiqueta tem uma classe de
etiqueta por padrão.
É fácil determinar o nome da classe do componente:
- Pegue o nome da classe de componente Java - por exemplo. Etiqueta
- Coloque o nome em minúsculas
- Se consistir em várias palavras, separe-as com o símbolo "-"
Alguns exemplos:
- Etiqueta → Etiqueta
- Caixa de seleção → caixa de seleção
Ao usar classes como seletores, adicione ".". Isso significa que o seletor para a classe
label é
.label .
Classes personalizadas
Se as classes internas não forem suficientes, você poderá adicionar suas próprias classes aos seus componentes. Você pode usar várias classes separadas por vírgula:
<Label styleClass="my-label,other-class">I am a simple label</Label>
Ou em Java:
Label label = new Label("I am a simple label"); label.getStyleClass().addAll("my-label", "other-class");
A adição de classes dessa maneira não remove a classe de componente por padrão (nesse caso,
rótulo ).
Há uma classe especial chamada
raiz . É o componente raiz da sua cena. Você pode usá-lo para estilizar tudo dentro da sua cena (por exemplo, defina uma fonte global). É semelhante ao uso do seletor de tags de corpo em HTML.
ID
Outra maneira de selecionar componentes no CSS é usar um identificador de componente (ID). É um identificador exclusivo para um componente. Diferentemente das classes que podem ser atribuídas a vários componentes, o identificador deve ser exclusivo na cena.
Enquanto o símbolo "." É usado para indicar a classe. na frente do nome em seus seletores, os identificadores são marcados com o símbolo "#".
#my-component { ... }
No FXML, você pode usar
fx: id para definir o identificador CSS de um componente.
<Label fx:id="foo">I am a simple label</Label>
No entanto, há uma ressalva. O mesmo
identificador é usado para se referir ao objeto componente declarado no seu controlador com o mesmo nome. Como o identificador e o nome do campo no controlador devem corresponder,
fx: id deve levar em consideração a restrição de nomenclatura Java para nomes de campos. Embora a convenção de nomenclatura CSS defina palavras individuais separadas por um caractere "-", é um caractere inválido para nomes de campos Java. Portanto, para
fx: id com poucas palavras, você precisa usar uma convenção de nomenclatura diferente, como CamelCase, ou usar sublinhado.
<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>
Em Java, você pode simplesmente chamar o método
setId () do seu componente.
Label label = new Label("I am a simple label"); label.setId("foo");
Propriedades
Embora o CSS usado no JavaFX seja muito semelhante ao CSS original da web, há uma grande diferença. Os nomes das propriedades são diferentes e existem muitas novas propriedades específicas do JavaFX. Eles têm o prefixo
-fx- .
Aqui estão alguns exemplos:
- -fx-background-color : cor de fundo
- -fx-text-fill : cor do texto
- -fx-font-size : tamanho do texto
Você pode encontrar uma lista de todas as propriedades no
guia oficial de design .
Pseudo-classes
Além das classes usuais que marcam componentes específicos, existem as chamadas pseudo-classes que indicam o estado de um componente. Pode ser, por exemplo, uma classe para marcar que o componente tem foco ou o cursor do mouse está nele.
Existem muitas pseudo-classes embutidas. Vamos olhar para o botão. Existem várias pseudo-classes que você pode usar, por exemplo:
- pairar : passe o mouse sobre o botão
- focado : o botão tem foco
- desabilitado : o botão está desabilitado
- pressionado : botão pressionado
As pseudo-classes começam com o caractere ":" (por exemplo
:: hover ) nos seletores de CSS. Obviamente, você precisa especificar a qual componente sua pseudo-classe pertence - por exemplo,
button: hover . O exemplo a seguir mostra um seletor que se aplica a todos os botões que têm foco:
.button:focused { -fx-background-color: red; }
Diferentemente do CSS, que possui apenas pseudo-classes básicas para estados como
foco e
foco , o JavaFX possui pseudo-classes específicas de componentes que se relacionam a diferentes estados ou propriedades de componentes.
Por exemplo:
- As barras de rolagem têm pseudo-classes horizontais e verticais
- Elementos (células) têm pseudo-classes ímpares e pares
- O TitledPane expandiu e reduziu as pseudo-classes.
Pseudo-classes personalizadas
Além das pseudo-classes integradas, você pode definir e usar suas próprias pseudo-classes.
Vamos criar nosso próprio rótulo (herdado da classe Label). Ele terá uma nova propriedade lógica chamada
brilhante . Nesse caso, queremos que nosso rótulo tenha uma pseudo
classe brilhante .
Como a tag possui uma pseudo
classe brilhante , podemos definir o plano de fundo da tag
gold :
.shiny-label:shiny { -fx-background-color: gold; }
Agora crie a própria classe.
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); } }
Existem várias partes importantes aqui:
- Temos a propriedade booleana BooleanProperty em vez da booleana usual. Isso significa que o objeto brilhante é observável e podemos rastrear (ouvir) alterações em seu valor.
- Registramos um ouvinte que será chamado toda vez que o valor do objeto brilhante mudar usando shiny.addListener () .
- Quando o valor brilhante muda, adicionamos / removemos a pseudo- classe brilhante, dependendo do valor atual de pseudoClassStateChanged (PseudoClass.getPseudoClass ("shiny"), shiny.get ()) .
- Adicionamos uma classe personalizada para todos os rótulos de rótulo brilhante , em vez de ter apenas a classe de rótulo herdada do pai. Assim, só podemos selecionar tags brilhantes .
Folha de estilos padrão
Mesmo se você não fornecer nenhum estilo, cada aplicativo JavaFX já possui alguns estilos visuais. Há uma folha de estilo padrão que se aplica a todos os aplicativos. É chamado
modena (desde o JavaFX 8, anteriormente era chamado
caspian ).
Esta folha de estilo pode ser encontrada no arquivo:
jfxrt.jar \ com \ sun \ javafx \ cena \ controle \ skin \ modena \ modena.cssOu você pode encontrar o arquivo
aqui . No mesmo diretório, existem muitas imagens usadas pela folha de estilo.
Essa folha de estilo fornece estilos padrão, mas tem a prioridade mais baixa sobre outros tipos de folhas de estilo, para que você possa substituí-la facilmente.
Folha de estilo da cena
Além da folha de estilo padrão mencionada acima, é claro que você pode fornecer sua própria. O nível mais alto no qual você pode aplicar a estilização é toda a cena. Você pode implementar isso no seu FXML:
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" stylesheets="styles.css" ... > ... </BorderPane>
Ou no seu código Java:
String stylesheet = getClass().getResource("/styles.css").toExternalForm(); scene.getStylesheets().add(stylesheet);
Preste atenção na chamada
paraExternalForm () . O Scene espera obter o conteúdo da folha de estilo como uma sequência, não como um arquivo, por isso precisamos fornecer o conteúdo da folha de estilo como uma sequência.
Folha de estilo principal
Além da folha de estilo para toda a cena, às vezes é útil ter estilos no nível do layout. Ou seja - para um contêiner separado, como VBox, HBox ou GridPane. O pai comum de todos os layouts é a classe pai, que define métodos para processar folhas de estilo no nível do layout. Esses estilos se aplicam apenas aos componentes desse layout e não à cena inteira. Um estilo no nível do layout tem precedência sobre um estilo no nível da cena.
<HBox stylesheets="styles.css"> ... </HBox>
Em Java, você precisa carregar o conteúdo da folha de estilo, como antes para a cena:
HBox box = new HBox(); String stylesheet = getClass().getResource("/styles.css").toExternalForm(); box.getStylesheets().add(stylesheet);
Estilos embutidos
Até agora, analisamos apenas os casos em que uma folha de estilo externa foi atribuída a uma cena ou layout inteiro. Mas você pode definir propriedades de estilo individuais no nível do componente.
Aqui você não precisa se preocupar com o seletor, pois todas as propriedades estão definidas para um componente específico.
Você pode especificar várias propriedades separadas por ponto e vírgula:
<Label style="-fx-background-color: blue; -fx-text-fill: white"> I'm feeling blue. </Label>
Em Java, você pode usar o método
setStyle () :
Label label = new Label("I'm feeling blue."); label.setStyle("-fx-background-color: blue; -fx-text-fill: white");
Os estilos no nível do componente têm precedência sobre os estilos de cena e os estilos pai no nível do layout.
Por que você precisa evitá-los
O estilo no nível do componente pode ser conveniente, mas é uma solução rápida e suja. Você está abandonando a principal vantagem do CSS, que é a separação de estilos de componentes. Agora você liga rigidamente seus elementos visuais diretamente aos componentes. Não é mais possível alternar facilmente suas folhas de estilo quando necessário; não é possível alterar os temas.
Além disso, você não tem mais um único local central onde seu estilo é definido. Quando você precisa alterar algo em um conjunto de componentes semelhantes, é necessário alterar cada um dos componentes separadamente e não editar apenas um local na folha de estilos externa. Portanto, os estilos de componentes em linha devem ser evitados.
Prioridades da folha de estilo
Você pode fornecer estilo em vários níveis - cena, pai, estilos embutidos e também há uma folha de estilo padrão do modem. Se você alterar a mesma propriedade do mesmo componente em vários níveis, o JavaFX possui uma configuração de prioridade que determina quais estilos devem ser usados. Lista de prioridades - da mais alta para a mais baixa:
- Estilos embutidos
- Estilos principais
- Estilos de cena
- Estilos padrão
Isso significa que, se você definir a cor de plano de fundo de um determinado rótulo no nível inline e na cena, o JavaFX utilizará o valor definido nos estilos inline, pois possui uma prioridade mais alta.
Leitura adicional
O JavaFX possui muitas propriedades CSS e sua descrição está além do escopo desta publicação; para obter uma lista detalhada, consulte o
guia de referência oficial do CSS para JavaFX .