Tradução do
Tutorial do
JavaFX: artigo FXML e SceneBuilder de Vojtech Ruzicka.
Como criar uma GUI com JavaFX usando a marcação FXML e o SceneBuilder.
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
Maneira tradicional
No artigo anterior,
criamos um aplicativo simples Hello World .
Apenas um lembrete - o código ficou assim:
@Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("Hello world Application"); primaryStage.setWidth(300); primaryStage.setHeight(200); InputStream iconStream = getClass().getResourceAsStream("/icon.png"); Image image = new Image(iconStream); primaryStage.getIcons().add(image); Label helloWorldLabel = new Label("Hello world!"); helloWorldLabel.setAlignment(Pos.CENTER); Scene primaryScene = new Scene(helloWorldLabel); primaryStage.setScene(primaryScene); primaryStage.show(); }
Como você pode ver, toda a interface do usuário é criada no código Java.
Este é um exemplo muito simples, mas conforme seu aplicativo se torna mais complexo, quando você precisa inserir vários níveis de layouts aninhados e muitos componentes, o código resultante pode se tornar muito difícil de entender. No entanto, isso não é tudo - na mesma classe há um código responsável pela estrutura, efeitos visuais e comportamento ao mesmo tempo.
A turma claramente não tem uma única responsabilidade. Compare isso, por exemplo, com a interface da web, onde cada página tem tarefas claramente separadas:
- HTML é uma estrutura
- CSS é efeitos visuais
- JavaScript é comportamento
Apresentando o FXML
Obviamente, ter todo o código em um só lugar não é uma boa ideia. Você precisa estruturá-lo de alguma forma para que seja mais fácil entender e torná-lo mais gerenciável.
Na realidade, existem muitos padrões de design para isso. Normalmente, você acaba com a opção "Model-View-Whatever" - é algo como "Model View Controller", "Model View Presenter" ou "Model View ViewModel".
Você pode passar horas discutindo os prós e contras de diferentes opções - não vamos fazer isso aqui. Mais importante, com o JavaFx, você pode usar qualquer um deles.
Isso é possível porque, além do design procedural da sua interface com o usuário, você pode usar a marcação XML declarativa.
Acontece que a estrutura hierárquica do XML é uma ótima maneira de descrever a hierarquia de componentes na interface do usuário. HTML funciona muito bem, certo?
O formato XML específico do JavaFX é chamado FXML. Nele, você pode definir todos os componentes do aplicativo e suas propriedades, além de associá-los ao controlador, responsável pelo gerenciamento das interações.
Baixar arquivos FXML
Então, como podemos mudar nosso método de inicialização para trabalhar com FXML?
FXMLLoader loader = new FXMLLoader(); URL xmlUrl = getClass().getResource("/mainScene.fxml"); loader.setLocation(xmlUrl); Parent root = loader.load(); primaryStage.setScene(new Scene(root)); primaryStage.show();
Aqui
raiz representa o componente raiz da sua interface com o usuário, outros componentes estão aninhados nela.
O método de
carregamento possui um valor de retorno genérico, para que você possa especificar um tipo específico, não
Pai . Em seguida, você obtém acesso a métodos orientados a componentes. No entanto, isso torna seu código mais frágil. Se você alterar o tipo de componente raiz em seu FXML, o aplicativo poderá parar de funcionar em tempo de execução, mas não haverá erros durante a compilação. Isso ocorre porque agora existe uma incompatibilidade do tipo declarado no seu FXML e no carregador Java FXML.
Criando arquivo FXML
Agora sabemos como carregar o arquivo FXML, mas ainda precisamos criá-lo. O arquivo deve ter a extensão .fxml. Em um projeto Maven, você pode colocar esse arquivo na pasta de recursos ou o FXMLLoader pode fazer o download de um URL externo.
Após criar o arquivo, insira a declaração XML em sua primeira linha:
<?xml version="1.0" encoding="UTF-8"?>
Importar
Antes de adicionar componentes individuais a um arquivo, você deve garantir que eles sejam reconhecidos corretamente. Para fazer isso, adicione instruções de importação. Isso é muito semelhante à importação em classes Java. Você pode importar classes individuais ou usar curingas como de costume. Vejamos um exemplo de uma seção de importação:
<?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?>
A boa notícia é que, em vez de adicionar todas as instruções de importação manualmente, seu IDE deve ajudá-lo a adicionar a importação, como adicioná-las às classes Java.
Adicionando componentes
Agora é hora de adicionar alguns componentes. Em um
artigo anterior, aprendemos que cada cena pode ter apenas um componente filho. Para começar, vamos adicionar um rótulo simples:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <Label>Hello World!</Label>
Obviamente, rotular como componente raiz não é um exemplo muito realista. Geralmente é preferível usar algum tipo de layout (layout), que é um contêiner para vários componentes e organiza sua organização. Abordaremos layouts mais adiante nesta série, mas, por enquanto, vamos usar um VBox simples que coloca seus filhos verticalmente uns sobre os outros.
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.control.Button?> <VBox> <Label text="Hello world!"/> <Label text="This is a simple demo application."/> <Button text="Click me!"/> </VBox>
Espaço para nome FX
Existem alguns elementos e atributos FXML que não estão disponíveis por padrão. Você precisa adicionar um FXML de espaço para nome para disponibilizá-lo. Ele deve ser adicionado ao componente raiz:
<?xml version="1.0" encoding="UTF-8"?> ... <VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"> ... </VBox>
Agora você pode usar os novos elementos do espaço para nome fx. Vamos tentar adicionar identificadores exclusivos aos nossos componentes:
<Label fx:id="mainTitle" text="Hello world!"/>
O atributo
fx: id é um identificador exclusivo para um componente que pode ser usado para se referir a um componente de outras partes do nosso FXML e até do nosso controlador.
Scripts
Nossa aplicação ainda é estática. Existem vários rótulos e um botão, mas o aplicativo não faz nada dinâmico.
Vamos responder ao clique do nosso botão e alterar o título de "Clique em mim!" Para "Clique em mim de novo!".
A primeira coisa a fazer é adicionar um manipulador de eventos onAction para o nosso botão.
<Button fx:id="mainButton" text="Click me!" onAction="buttonClicked()"/>
Preste atenção ao fx: id, este é o identificador que será usado posteriormente para se referir ao botão.
Agora você precisa fornecer uma função que será chamada para manipular o evento. Pode ser definido dentro da tag fx: script. O importante é que você pode usar idiomas diferentes para escrever um script, JavaScript, Groovy ou Clojure. Vejamos um exemplo em JavaScript:

Observe que nos referimos ao nosso componente Button usando o identificador mainButton, que foi declarado assim:
fx:id = "mainButton"
Você também deve indicar qual linguagem de script você usa no arquivo FXML:
<?language javascript?>
Vejamos o texto completo do exemplo:

Devo usar isso?
O exemplo acima mostra como fazer referência a componentes usando
fx: id e como adicionar um comportamento simples usando um script JavaScript. É isso mesmo que você deveria estar fazendo?
A resposta é na maioria dos casos não. Existem vários problemas com essa abordagem. A razão pela qual o FXML foi introduzido foi uma separação de interesses - para separar a estrutura e o comportamento da interface do usuário. Nesse script, o comportamento foi mesclado com a estrutura da interface do usuário novamente retornada. Além disso, como não trabalhamos mais com código Java, mas com XML, todas as verificações de código em tempo de compilação e segurança de tipo foram perdidas. Agora, todos os problemas no aplicativo serão detectados no tempo de execução e não no tempo de compilação. O aplicativo tornou-se muito frágil e propenso a erros.
Adicionando um controlador
Então, o que pode ser feito para obter uma clara separação de interesses? Você pode vincular o controlador ao nosso arquivo FXML. Um controlador é uma classe Java responsável por manipular o comportamento e a interação do usuário em um aplicativo. Dessa forma, você pode retornar as verificações de segurança de tipo e tempo de compilação.
O controlador é um POJO, não deve estender ou implementar nada, nem deve ter anotações especiais.
Como posso associar uma classe de controlador ao nosso FXML? Existem essencialmente duas opções.
Em Java
Você mesmo pode criar uma instância do controlador ou usar outros métodos para criar uma instância, como injeção de dependência. Em seguida, basta baixar o seu
FXMLLoader .
FXMLLoader loader = new FXMLLoader(); loader.setController(new MainSceneController());
Em FXML
Você pode especificar a classe do seu controlador como um atributo
fx: controller , que deve estar localizado no componente raiz.
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> ... </VBox>
Se você declarar sua classe Controller em FXML, ela será criada automaticamente para você. Essa abordagem tem uma limitação: no controlador, você precisa criar um construtor sem argumentos para facilitar a criação de uma nova instância da classe Controller.
Para acessar uma instância de um controlador criado automaticamente, você pode usar o carregador FXML:
FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("/mainScene.fxml")); MainSceneController controller = loader.getController();
Chamando métodos do controlador
Agora que você possui um controlador, é possível excluir o script e implementar a lógica para pressionar os botões diretamente no controlador:
public class MainSceneController { public void buttonClicked() { System.out.println("Button clicked!"); } }
O próximo passo é registrar a chamada desse método como o
manipulador de eventos
onAction do nosso botão. Para se referir aos métodos do nosso controlador, precisamos usar o sinal
# na frente do nome do método:
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> <Label fx:id="mainTitle" text="Hello world!"/> <Label fx:id="subTitle" text="This is a simple demo application."/> <Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/> </VBox>
Quando um botão é clicado, ele chama o método
MainSceneController.buttonClicked () . Lembre-se de que isso só funciona se o método for declarado público. Se o modificador de acesso for mais rigoroso, você deve anotar o método com a anotação
@FXML .
@FXML private void buttonClicked() { System.out.println("Button clicked!"); }
Incorporando componentes em um controlador
Até agora, estamos apenas imprimindo no console. E se quisermos mudar o texto do nosso botão para "
Clique em mim novamente "
novamente ? Como podemos obter links para componentes em nosso controlador?
Felizmente, isso é fácil. Lembre-se desses atributos
fx: id ?
<Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/>
O JavaFX tenta combinar automaticamente os componentes com
fx: id com os campos definidos no seu controlador com o mesmo nome.
Suponha que tenhamos um botão descrito acima com
fx:id="mainButton"
O JavaFX tenta injetar o objeto de botão no seu controlador em um campo chamado
mainButton :
public class MainSceneController {
Como nos métodos anteriores, seus campos devem ser
públicos ou anotados em
@FXML .
Agora que temos um link para o nosso botão, podemos alterar facilmente o texto:
public class MainSceneController { @FXML private Button mainButton; @FXML private void buttonClicked() { mainButton.setText("Click me again!"); } }
Construtor de cenas
Escrever sua estrutura da GUI em XML pode ser mais natural do que em Java (especialmente se você estiver familiarizado com HTML). No entanto, ainda não é muito conveniente. A boa notícia é que existe uma ferramenta oficial chamada Scene Builder que o ajudará a criar a interface do usuário. Em poucas palavras, este é um editor gráfico para sua GUI.

O editor possui três áreas principais:
- A parte esquerda exibe os componentes disponíveis que podem ser arrastados para a parte do meio. Ele também contém uma hierarquia de todos os componentes em sua interface do usuário, para que você possa navegar facilmente.
- A parte do meio é seu aplicativo exibido com base no seu arquivo FXML.
- À direita está o atual inspetor de componentes. Aqui você pode editar várias propriedades do componente atual selecionado. Qualquer componente selecionado no meio da hierarquia é exibido no inspetor.
Autônomo
O Scene Builder pode ser
baixado como um aplicativo independente que pode ser usado para editar arquivos FXML.
Integração com o IntelliJ IDEA
Como alternativa, o Scene Builder oferece integração com o IDE.
No IntelliJ IDEA, você pode clicar com o botão direito do mouse em qualquer arquivo FXML e selecionar a opção de menu Abrir no SceneBuilder.
Como alternativa, o IntelliJ IDEA integra o SceneBuilder diretamente no IDE. Se você abrir o arquivo FXML no IDEA, duas guias aparecerão na parte inferior da tela
Para cada arquivo FXML, você pode alternar facilmente entre editar o arquivo FXML diretamente ou através do SceneBuilder.

No IntelliJ IDEA, você pode configurar o local do executável do SceneBuilder:
Settings → Languages & Frameworks → JavaFX → Path to SceneBuilder
O que vem a seguir
No próximo post de nossa série, discutiremos alguns layouts básicos que podem ser usados para organizar os componentes de um aplicativo GUI no JavaFX.