javaFX - alandrade21/docsCompartilhados GitHub Wiki

Nesta página:

Maven

Ao criar um projeto novo, o template org.openjfx.javafx-archetype-fxml do maven central.

Um projeto javaFX tem as seguintes dependências:

<dependency>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-controls</artifactId>
  <version>19</version>
</dependency>
<dependency>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-fxml</artifactId>
  <version>19</version>
</dependency>
<dependency>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-web</artifactId>
  <version>19</version>
</dependency>

javafx.base é uma dependência transitiva. Outros pacotes podem ser acrescidos, a depender das necessidades, como javafs.graphics, ou suporte à 3D.

Ainda é necessário o plugin abaixo:

<plugin>
  <groupId>org.openjfx</groupId>
  <artifactId>javafx-maven-plugin</artifactId>
  <version>0.0.6</version>
  <executions>
    <execution>
      <!-- Default configuration for running -->
      <!-- Usage: mvn clean javafx:run -->
      <id>default-cli</id>
      <configuration>
        <mainClass>br.eti.alandrade21.myApp/br.eti.alandrade21.myApp.Launcher</mainClass>
      </configuration>
    </execution>
    <execution>
      <!-- Configuration for debugging -->
      <id>debug</id>
      <configuration>
        <options>
          <option>-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8000</option>
        </options>
        <mainClass>br.eti.alandrade21.myApp/br.eti.alandrade21.myApp.Launcher</mainClass>
      </configuration>
    </execution>
  </executions>
</plugin>

Essa configuração define qual é a classe com o método main, no formato nome-do-módulo/nome-da-classe.

No projeto é necessário a definição do module-info.java. Um exemplo abaixo:

module br.eti.alandrade21.myApp {
  requires javafx.controls;
  requires javafx.fxml;
  requires javafx.web;
  requires javafx.graphics;

  opens br.eti.alandrade21.myApp to javafx.fxml;
  opens br.eti.alandrade21.myApp.controller to javafx.fxml;

  exports br.eti.alandrade21.myApp;
}

O primeiro bloco declara quais pacotes do jFX serão utilizados pelo seu sistema.

O javaFX faz uma série de reflections com o seu código. Para permitir esses reflections é necessário as cláusulas open.

Estrutura JavaFX e Ciclo de Vida

package br.eti.alandrade21.myApp;

import javafx.application.Application;
import javafx.stage.Stage;
import java.Exception;

/**
 * JavaFX App
 */
public class Launcher extends Application {
  
  public static void main(String[] args) {
      launch();
  }

  @Override
  public void start(Stage stage) throws Exception {
    
    // Disparo da aplicação.
  }
}

O método launch() só retorna qdo todas as janelas são fechadas (javafx.application.Platform.setImplicitExit(true), o que vem setado por padrão), ou quando javafx.application.Platform.exit() é chamado.

Após o disparo do método launch(), o javaFX chama os seguintes métodos da classe que herda de Applications:

  • O construtor padrão.
  • O método init().
  • O método start(stage).
  • O método stop().

init e stop estão vazios na classe Application.

Passagem de Parâmetros para a Aplicação

@Override
public void start(Stage stage) {
  // Get application parameters
  Parameters p = this.getParameters();
  Map<String, String> namedParams = p.getNamed();
  List<String> unnamedParams = p.getUnnamed();
  List<String> rawParams = p.getRaw();

  ...
}

Parâmetros nomeados são passados na forma --key=value. Então na seguinte linha de parâmetros ... Anna Lola --width=200 --heigh=100:

  • Há dois parâmetros não nomeados e dois nomeados.
  • A lista de parâmetros raw conteria algo como [Anna, Lola, --width=200, --height=100].

JavaFX com Spring Boot

As informações condensadas nesta seção fora retiradas de:

POM

Ao configurar o POM do projeto é necessário inserir um starter adicional ao spring-boot-starter. Neste exemplo usamos o spring-boot-starter-data-jpa.

Por segurança, recomenda-se colocar a entrada spring.main.web-application-type=none no arquivo application.properties. Caso o módulo web do spring acabe no module path, o projeto não será considerado um projeto web pelo spring.

O spring-boot-maven-plugin fica configurado da seguinte maneira:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <configuration>
    <excludes>
      <exclude>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
      </exclude>
    </excludes>
    <executable>true</executable>
  </configuration>
</plugin>

Essa configuração faz com que o jar gerado contenha todos os jars das dependências em uso no projeto (com exceção do jar do lombok).

A entrada <executable>true</executable> faz com que, ao executar o comando mvn clean package, o jar gerado seja executável a partir do prompt de comando (com o comando ./meujar.jar).

module-info

Para que o projeto seja compilado corretamente, é necessário importar vários módulos do spring, e abrir os módulos do projeto para esses módulos do spring. Abaixo, um exemplo:

module br.eti.alandrade21.meuProjeto {
  requires javafx.controls;
  requires javafx.fxml;
	requires spring.boot.autoconfigure;
	requires spring.boot;
	requires spring.context;
	requires spring.beans;
	requires spring.core;
	requires javafx.graphics;

  opens br.eti.alandrade21.meuProjeto to javafx.fxml, spring.core;
  opens br.eti.alandrade21.meuProjeto.pages to spring.beans, spring.core;
    
  exports br.eti.alandrade21.meuProjeto;
}

Configuração do Projeto

Num projeto JavaFX com SpringBoot, o ideal é deixar o bootstrap da aplicação por conta do JavaFX, e deixar o JavaFX fazer o bootstrap do Spring.

Assim, na classe que contiver o método main, ao invés de fazermos SpringApplication.run(AppSpring.class, args); como de praxe, vamos inicializar o JavaFX com o uso da classe javafx.application.Application;, conforme mostrado no exemplo abaixo:

package br.eti.alandrade21.meuProjeto;

import javafx.application.Application;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;

@EnableAutoConfiguration(exclude = {
	DataSourceAutoConfiguration.class, 
	DataSourceTransactionManagerAutoConfiguration.class, 
	HibernateJpaAutoConfiguration.class
})
@SpringBootApplication
public class AppSpring {

    public static void main(String[] args) {
    	Application.launch(AppFX.class, args);
    }

}

A anotação @EnableAutoConfiguration acima foi colocada apenas para fazer com que a aplicação rode sem a necessidade de fazer a configuração do JPA. Feita a configuração, ela pode ser retirada.

A classe AppFX referenciada acima é a classe que herda de javafx.application.Application e possui o método start. O código dessa classe é mostrado abaixo:

package br.eti.alandrade21.meuProjeto;

import java.io.IOException;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import br.eti.alandrade21.meuProjeto.config.StageReadyEvent;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;

public class AppFX extends Application {
	
	private ConfigurableApplicationContext springContext;

	@Override
	public void init() throws Exception {
		
		ApplicationContextInitializer<GenericApplicationContext> initializer =
			ac -> {
				ac.registerBean(Application.class, () -> AppFX.this);
			};
		
		this.springContext = new SpringApplicationBuilder()
				.sources(AppSpring.class)
				.initializers(initializer)
				.run(getParameters().getRaw().toArray(new String[0]));
	}

	@Override
	public void start(Stage primaryStage) throws Exception {
		
		this.springContext.publishEvent(new StageReadyEvent(primaryStage));
	}

	@Override
	public void stop() throws Exception {
		this.springContext.close();
	    Platform.exit();
	}
}

O método init faz o bootstrap do spring, criando o contexto do string e registrando os objetos que forem necessários do JavaFX como spring beans. No exemplo acima apenas a própria classe AppFX é registrada como um spring bean.

Ao iniciar o contexto spring, os parâmetros de disparo da aplicação são passados para o spring.

De forma similar, o método stop faz o encerramento do contexto spring.

No método start é disparado um evento para avisar ao spring que a parte JavaFX já está pronta, e publicando o primaryStage criado durante a inicialização do JavaFX. O evento disparado é do tipo StageReadyEvent, implementado na própria aplicação. Essa classe é mostrada abaixo:

package br.eti.alandrade21.meuProjeto.config;

import org.springframework.context.ApplicationEvent;
import javafx.stage.Stage;

public class StageReadyEvent extends ApplicationEvent {
	
	private static final long serialVersionUID = 1L;

	public StageReadyEvent(Stage source) {
		super(source);
    }
	
	public Stage getStage() {
		return (Stage)getSource();
	}
}

O passo final é criar uma classe responsável por interceptar esse evento e criar a primeira janela da aplicação, conforme mostrado abaixo:

package br.eti.alandrade21.meuProjeto.pages;

import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import br.eti.alandrade21.meuProjeto.PrimaryController;
import br.eti.alandrade21.meuProjeto.config.StageReadyEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

@Component
public class ViewFactory implements ApplicationListener<StageReadyEvent>{
	
	@Autowired
	PrimaryController primaryController;

	@Override
	public void onApplicationEvent(StageReadyEvent event) {
		Stage stage = event.getStage();
		
		FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("primary.fxml"));
		fxmlLoader.setController(primaryController);
		Parent parent;
		try {
			parent = fxmlLoader.load();
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}
		Scene scene = new Scene(parent);
		stage.setScene(scene);
		stage.show();
	}
}

Problemas Debugando JavaFX no Eclipse

Debugar JavaFX no linux pode causar o travamento do gnome, deixando o mouse se mexer mas não deixando clicar em nenhum elemento.

Se esse for o caso, vá na configuração de debug e acrescente o argumento de VM -Dsun.awt.disablegrab=true.

Problemas com Autocomplete de CSS do JavaFX

Caso o editor de CSS não reconheça as classes e elementos CSS do JavaFX, será necessário ir às propriedades do projeto no eclipse, na opção Java Build Path e adicionar um novo jar ao classpath. Esse jar é o org.eclipse.fx.ide.css.jfx8_<número>.jar, que fica na raiz da pasta de plugins dentro da pasta de instalação do eclipse.

⚠️ **GitHub.com Fallback** ⚠️