Ajuda, como entender e usar o : <fx:root> , no JavaFX? com Scene Builder

9 respostas Resolvido
javafxjava
I

Olá!
Andei lendo um artigo:
https://stackoverflow.com/questions/23600926/how-to-understand-and-use-fxroot-in-javafx

porém não me ficou muito claro, alguém sabe sobre essa funcionalidade do JavaFX?
Desde já muito Obrigado!

9 Respostas

A
Solucao aceita

Olá, essa tag em resumo serve para informar ao Java qual componente será o root, ou seja, o container em que o layout FXML será renderizado.

Sem essa tag o Java cria uma instância do container definido e injeta o layout nela, ex: se sua tela esiver dentro de um AnchorPane quando for carregar esse layout será retornada uma instância de AnchorPane no método FXMLLoader.getRoot().

Caso informada essa tag ex: <fx:root type="javafx.scene.layout.AnchorPane"> você poderá criar sua própria instância de um AnchorPane e passar para o loader no método FXMLLoader.setRoot(), dessa forma o layout será “injetado” dentro da sua intrância.

Pelo que entendi da resposta isso só é possível através dessa tag.

Obs.: Nunca usei essa tag, por desconhecer, mas já tive casos em que ela seria bem útil.

I

Muito Obrigado!
:smiley:

A

O Andrauss, disse tudo.

Como estou aprendendo JavaFX, tive curiosidade de ver o funcionamento desta tag.
Eu estava procurando algo assim, pois permite utilizar os construtores com parâmetros, além de facilitar a criação de componentes personalizados, pois o css é assumido fora da classe e pode ser manipulado dentro desta, FACILITANDO efeitos na aplicação, fora outras possibilidades com reaproveitamente de código.

Pra mim, as vantagens são significativa pois só de poder ** utilizar construtores com parêmetros**, posso remover parte da camada de abstração que realizava a comunicação entre os controllers.

A melhor comparação que tenho em mente é você acaba programando de forma muito próxima ao swing.

Pequeno teste que realizei:

package fxroot;

import fxroot.view.MainController;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class FxRoot extends Application {
    
    private MainController mainController;
    
    @Override
    public void start(Stage primaryStage) {
        mainController = new MainController("Testanto fxRoot", 1);
        Scene scene = new Scene(mainController, 300, 250);        
        primaryStage.setTitle(mainController.getTitutlo()+" "+mainController.getValor());
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }
    
}

.

package fxroot.view;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;

/**
 * FXML Controller class
 */
public class MainController extends AnchorPane {

    private final String titulo;
    private int valor;
    @FXML
    private TextField txtInteragir;
    @FXML
    private Label lblInteragir;
    @FXML
    private Button btnInteragir;
    @FXML
    private FXMLLoader loader;

    public MainController(String informacao, int valor) {
        this.titulo = informacao;
        this.valor = valor;
        carregarFXML();
        interagir();
    }

    private void carregarFXML() {
        try {
            loader = new FXMLLoader(getClass().getResource("main.fxml"));
            loader.setController(this);
            loader.setRoot(this);
            loader.load();
        } catch (IOException ex) {
            Logger.getLogger(MainController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void interagir() {
        btnInteragir.setOnAction(seClicar -> {
            lblInteragir.setText("Interação[" + valor++ + "]" + txtInteragir.getText());
        }
        );
    }

    public int getValor() {
        return valor;
    }

    public String getTitulo() {
        return titulo;
    }

}

.//arquivo main.css não tem “nada”

.mainFxmlClass {

}

.

//arquivo main.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.URL?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>

<fx:root id="AnchorPane" prefHeight="400.0" prefWidth="600.0" styleClass="mainFxmlClass" type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111">
    <stylesheets>
        <URL value="@main.css" />
    </stylesheets>
   <children>
      <Button fx:id="btnInteragir" layoutX="228.0" layoutY="67.0" mnemonicParsing="false" text="Interagir" />
      <Label fx:id="lblInteragir" layoutX="47.0" layoutY="133.0" text="Label" />
      <TextField fx:id="txtInteragir" layoutX="47.0" layoutY="67.0" />
   </children>
</fx:root>
A

Interessante, nunca tinha usado essa tag. Sua abordagem é muito boa para criação de janelas e views.

A

Alterei um pequeno projeto é e realmente como estava imaginando.
Consegui reaproveitar muito do padrão anterior e eliminei a maior parte da abstração que fazia a comunicação entre os controllers, tudo isso porque os construtores agora recebem parâmetros.

Pra mim, foi um ganho considerável, pricipalmente em velocidade de desenvolvimento e manutenção.

O padrão que que passo a usar é bem adaptável, sendo uma variação do anterior.
Eis o padrão:

package ambiente;

import javafx.application.Application;
import javafx.stage.Stage;
import view.Cenario;
import view.main.MainController;

public class Ambiente extends Application {

    private Stage palco;

    @Override
    public void start(Stage primaryStage) {
        MainController mainController = new MainController(this);
        palco = primaryStage;
        atualizarScene(mainController.getCenario());
    }


    public void atualizarScene(Cenario cenario) {
        atualizarFechamento(cenario);
        palco.setScene(cenario.getCena());
        palco.setTitle(cenario.getTitulo());
        palco.setResizable(cenario.isResisable());
        palco.show();
    }

    private void atualizarFechamento(Cenario cenario) {
        palco.setOnCloseRequest((event) -> {
            if (!cenario.sair()) {
                event.consume();
                return;
            }
            //Serializar.gravar();
        });
    }

    public Stage getPalco() {
        return palco;
    }
    
    public static void main(String[] args) {
        launch(args);  
    }
    
}

.

package view;

import java.io.IOException;

import java.util.logging.Level;

import java.util.logging.Logger;

import javafx.fxml.FXMLLoader;

import javafx.scene.Scene;

import javafx.scene.layout.AnchorPane;
public class Cenario extends AnchorPane {

    private String titulo;
    private final String urlFXML;
    private Scene cena;
    private boolean sair, resisable;

    public Cenario(String titulo, String urlFXML) {
        this.titulo = titulo;
        this.urlFXML = urlFXML.endsWith(".fxml")? urlFXML: urlFXML+".fxml";
    }

    public Scene getCena() {
        return cena;
    }
    /**este método é responsável encerrar a aplicação
     * @return true ou false
     * @see ambiente.Ambiente#atualizarFechamento
     */
    public boolean sair() {
        return sair;
    }

    public void setSair(boolean sair) {
        this.sair = sair;
    }

    public String getTitulo() {
        return titulo;
    }

    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    protected final void carregarFXML(Controller controller) {
        try {
            FXMLLoader loader = new FXMLLoader(controller.getClass().getResource(urlFXML));
            loader.setController(controller);
            loader.setRoot(this);
            this.cena = new Scene(loader.load());
        } catch (IOException ex) {
            Logger.getLogger(Cenario.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public boolean isResisable() {
        return resisable;
    }

    public void setResisable(boolean resisable) {
        this.resisable = resisable;
    }

}

.

package view;

import ambiente.Ambiente;

public class Controller{
    private final Cenario cenario;
    protected final Ambiente ambienteExecucao;

    public Controller(Cenario cenario, Ambiente ambienteExecucao) {
        this.cenario = cenario;
        this.cenario.carregarFXML(Controller.this);
        this.ambienteExecucao = ambienteExecucao;
    }

    public Cenario getCenario() {
        return cenario;
    }

    public Ambiente getAmbienteExecucao() {
        return ambienteExecucao;
    }
}

.

package view;

import java.util.Arrays;
import javafx.scene.control.TextField;

public interface Formulario {

    boolean validar();

    void submeter();

    void clearForm();

    default void limparCampos(TextField... campos) {
        Arrays.stream(campos).forEach(campo -> campo.setText(""));
    }

}

.

package view.main;

import ambiente.Ambiente;
import java.net.URL;
import java.util.Arrays;
import java.util.ResourceBundle;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import view.Cenario;
import view.Controller;
import view.Formulario;
import view.other.OtherController;

public class MainController extends Controller implements Initializable, Formulario {
    
    @FXML
    private Button btnInformarPastaRaiz, btnInformarPastaDestinoScript, btnUnificarScript;
    @FXML
    private ComboBox<String> comboLinguagem;

    public MainController(Ambiente ambienteExecucao) {
        super(new Cenario("Ekemera Stage", "main.fxml"), ambienteExecucao);
    }
    
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        desabilitarBotoes();
        btnInformarPastaRaiz.setOnAction(onAction -> abrirPastaScripts());
        btnInformarPastaDestinoScript.setOnAction(onAction -> abrirPastaScripts());
        btnUnificarScript.setOnAction(onAction -> unificarScript());
        configurarComboLinguagem();

    }

    private void desabilitarBotoes() {
        desabilitarBotoes(true, btnInformarPastaDestinoScript, btnInformarPastaRaiz, btnUnificarScript);
    }

    private static void desabilitarBotoes(boolean desabilitar, Button... botoes) {
        Arrays.asList(botoes).forEach(botao -> botao.disableProperty().set(desabilitar));
    }

    private void configurarComboLinguagem() {
        comboLinguagem.getItems().addAll("Selecione uma linguagem","Java","JavaScript");
        comboLinguagem.getSelectionModel().select(0);
        comboLinguagem.valueProperty().addListener(
                (ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
                    desabilitarBotoes(comboLinguagem.getSelectionModel().getSelectedIndex() == 0, btnInformarPastaRaiz);
                    desabilitarBotoes(true, btnInformarPastaDestinoScript, btnUnificarScript);
                }
        );
    }

    private void unificarScript() {

    }
    
    //passando o ambiente de execução por meio dos construtores
    private void abrirPastaScripts() {
        desabilitarBotoes(!linguagemSelecionada().contains("cript"), btnInformarPastaDestinoScript, btnUnificarScript);
        OtherController outraJanela = new OtherController(ambienteExecucao);
        ambienteExecucao.atualizarScene(outraJanela.getCenario());
    }

    private String linguagemSelecionada() {
        return comboLinguagem.getSelectionModel().getSelectedItem();
    }
    
    @Override
    public boolean validar() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void submeter() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void clearForm() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
    
}

.//main.css

.paneRed{
    -fx-background-color: rgb(250,20,20);
}

.//main.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.URL?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>

<fx:root type="javafx.scene.layout.AnchorPane" prefHeight="400.0" prefWidth="600.0" styleClass="mainFxmlClass" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
    <stylesheets>
        <URL value="@main.css" />
        <URL value="@padrao.css" />
    </stylesheets>
   <children>
      <Pane fx:id="panePrincipal" prefHeight="400.0" prefWidth="600.0" styleClass="paneRed">
         <children>
            <Button fx:id="btnInformarPastaRaiz" layoutX="56.0" layoutY="92.0" maxHeight="50.0" maxWidth="238.0" mnemonicParsing="false" prefHeight="50.0" prefWidth="238.0" styleClass="buttonTopImage" text="Informar pasta raiz dos códigos Fonte" textAlignment="CENTER" textOverrun="CLIP" />
            <Button fx:id="btnInformarPastaDestinoScript" layoutX="163.0" layoutY="174.0" maxHeight="50.0" maxWidth="238.0" mnemonicParsing="false" prefHeight="50.0" prefWidth="238.0" styleClass="buttonTopImage" text="Informar pasta destino" textAlignment="CENTER" textOverrun="CLIP" />
            <Button fx:id="btnUnificarScript" layoutX="271.0" layoutY="259.0" maxHeight="50.0" maxWidth="238.0" mnemonicParsing="false" prefHeight="50.0" prefWidth="238.0" styleClass="buttonTopImage" text="..." textAlignment="CENTER" textOverrun="CLIP" />
            <ComboBox fx:id="comboLinguagem" layoutX="56.0" layoutY="47.0" prefHeight="25.0" prefWidth="238.0" />
         </children>
      </Pane>
   </children>
</fx:root>

.//padrao.css não tem nada

package view.other;

import ambiente.Ambiente;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;
import view.Cenario;
import view.Controller;

public class OtherController extends Controller implements Initializable {

    public OtherController(Ambiente ambienteExecucao) {
        super(new Cenario("Outro cenário", "other.fxml"), ambienteExecucao);
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
    }    
    
}

.//other.css não tem nada

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.URL?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>

<fx:root prefHeight="400.0" prefWidth="600.0" styleClass="mainFxmlClass" type="javafx.scene.layout.AnchorPane" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111">
    <stylesheets>
        <URL value="@other.css" />
    </stylesheets>
   <children>
      <Label layoutX="105.0" layoutY="123.0" text="Easy" />
   </children>
</fx:root>
A

Interessante, fica tudo numa só tela, dessa forma fica fácil aplicar transições e animações entre as telas e passar parâmetros. Vou aproveitar pra atualizar a minha lib, pois isso vai resolver alguns problemas que encontrei no desenvolvimento dela.

I

Meu amigo, muita coisa se esclareceu para mim agora. Obrigado!
Você diria que fazendo dessa forma é melhor do que usando a tag <fx:include> ?

A

Eu nunca usei a essa tag fx:include.
Faz menos de 2 meses que comecei a estudar JavaFX, pois havia decidido que faria a última prova com esta tecnologia.

O que posso dizer é que talvez, pois não exergo concorrência destrutiva e entre as tags.

Use as duas.

O que já era divertido, ficou melhor.

I

Perfeito, novamente obrigado!

Criado 19 de outubro de 2017
Ultima resposta 25 de out. de 2017
Respostas 9
Participantes 3